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 | struct Tag1{}; |
50 | |
51 | struct S { |
52 | void constMethod() const; |
53 | void nonConstMethod(); |
54 | |
55 | static void staticMethod(); |
56 | |
57 | void operator()(ConstTag) const; |
58 | void operator()(NonConstTag); |
59 | |
60 | void operator[](int); |
61 | void operator[](int) const; |
62 | |
63 | int& at(int); |
64 | const int& at(int) const; |
65 | const int& at(Tag1); |
66 | |
67 | int& weird_overload(); |
68 | const double& weird_overload() const; |
69 | |
70 | bool operator==(const S&) const; |
71 | |
72 | int int_member; |
73 | // We consider a mutation of the `*ptr_member` to be a const use of |
74 | // `*this`. This is consistent with the semantics of `const`-qualified |
75 | // methods, which prevent modifying `ptr_member` but not `*ptr_member`. |
76 | int* ptr_member; |
77 | |
78 | }; |
79 | |
80 | struct Derived : public S { |
81 | |
82 | }; |
83 | |
84 | void useVal(S); |
85 | void useRef(S&); |
86 | void usePtr(S*); |
87 | void usePtrPtr(S**); |
88 | void usePtrConstPtr(S* const*); |
89 | void useConstRef(const S&); |
90 | void useConstPtr(const S*); |
91 | void useConstPtrRef(const S*&); |
92 | void useConstPtrPtr(const S**); |
93 | void useConstPtrConstRef(const S* const&); |
94 | void useConstPtrConstPtr(const S* const*); |
95 | |
96 | void useInt(int); |
97 | void useIntRef(int&); |
98 | void useIntConstRef(const int&); |
99 | void useIntPtr(int*); |
100 | void useIntConstPtr(const int*); |
101 | |
102 | )" ; |
103 | |
104 | std::string Code = (CommonCode + Snippet).str(); |
105 | |
106 | llvm::SmallVector<StringRef, 1> Parts; |
107 | StringRef(Code).split(A&: Parts, Separator: "/*const*/" ); |
108 | |
109 | EXPECT_EQ(Code, |
110 | runCheckOnCode<ConstReferenceDeclRefExprsTransform<Indirections>>( |
111 | join(Parts, "" ))); |
112 | } |
113 | |
114 | TEST(ConstReferenceDeclRefExprsTest, ConstValueVar) { |
115 | RunTest<0>(Snippet: R"( |
116 | void f(const S target) { |
117 | useVal(/*const*/target); |
118 | useConstRef(/*const*/target); |
119 | useConstPtr(&/*const*/target); |
120 | useConstPtrConstRef(&/*const*/target); |
121 | /*const*/target.constMethod(); |
122 | /*const*/target.staticMethod(); |
123 | /*const*/target(ConstTag{}); |
124 | /*const*/target[42]; |
125 | useConstRef((/*const*/target)); |
126 | (/*const*/target).constMethod(); |
127 | /*const*/target.staticMethod(); |
128 | (void)(/*const*/target == /*const*/target); |
129 | (void)/*const*/target; |
130 | (void)&/*const*/target; |
131 | (void)*&/*const*/target; |
132 | /*const*/target; |
133 | S copy1 = /*const*/target; |
134 | S copy2(/*const*/target); |
135 | /*const*/target.int_member; |
136 | useInt(/*const*/target.int_member); |
137 | useIntConstRef(/*const*/target.int_member); |
138 | useIntPtr(/*const*/target.ptr_member); |
139 | useIntConstPtr(&/*const*/target.int_member); |
140 | |
141 | const S& const_target_ref = /*const*/target; |
142 | const S* const_target_ptr = &/*const*/target; |
143 | } |
144 | )" ); |
145 | } |
146 | |
147 | TEST(ConstReferenceDeclRefExprsTest, ConstRefVar) { |
148 | RunTest<0>(Snippet: R"( |
149 | void f(const S& target) { |
150 | useVal(/*const*/target); |
151 | useConstRef(/*const*/target); |
152 | useConstPtr(&/*const*/target); |
153 | useConstPtrConstRef(&/*const*/target); |
154 | /*const*/target.constMethod(); |
155 | /*const*/target.staticMethod(); |
156 | /*const*/target(ConstTag{}); |
157 | /*const*/target[42]; |
158 | useConstRef((/*const*/target)); |
159 | (/*const*/target).constMethod(); |
160 | (void)(/*const*/target == /*const*/target); |
161 | (void)/*const*/target; |
162 | (void)&/*const*/target; |
163 | (void)*&/*const*/target; |
164 | /*const*/target; |
165 | S copy1 = /*const*/target; |
166 | S copy2(/*const*/target); |
167 | /*const*/target.int_member; |
168 | useInt(/*const*/target.int_member); |
169 | useIntConstRef(/*const*/target.int_member); |
170 | useIntPtr(/*const*/target.ptr_member); |
171 | useIntConstPtr(&/*const*/target.int_member); |
172 | (void)/*const*/target.at(3); |
173 | |
174 | const S& const_target_ref = /*const*/target; |
175 | const S* const_target_ptr = &/*const*/target; |
176 | (void)/*const*/target.at(3); |
177 | } |
178 | )" ); |
179 | } |
180 | |
181 | TEST(ConstReferenceDeclRefExprsTest, DEBUGREMOVEME) { |
182 | RunTest<0>(Snippet: R"( |
183 | void f(S target, const S& other) { |
184 | S* target_ptr = ⌖ |
185 | } |
186 | )" ); |
187 | } |
188 | |
189 | TEST(ConstReferenceDeclRefExprsTest, ValueVar) { |
190 | RunTest<0>(Snippet: R"( |
191 | void f(S target, const S& other) { |
192 | useConstRef(/*const*/target); |
193 | useVal(/*const*/target); |
194 | useConstPtr(&/*const*/target); |
195 | useConstPtrConstRef(&/*const*/target); |
196 | /*const*/target.constMethod(); |
197 | /*const*/target.staticMethod(); |
198 | target.nonConstMethod(); |
199 | /*const*/target(ConstTag{}); |
200 | /*const*/target[42]; |
201 | /*const*/target(ConstTag{}); |
202 | target(NonConstTag{}); |
203 | useRef(target); |
204 | usePtr(&target); |
205 | useConstRef((/*const*/target)); |
206 | (/*const*/target).constMethod(); |
207 | (void)(/*const*/target == /*const*/target); |
208 | (void)(/*const*/target == other); |
209 | (void)/*const*/target; |
210 | (void)&/*const*/target; |
211 | (void)*&/*const*/target; |
212 | /*const*/target; |
213 | S copy1 = /*const*/target; |
214 | S copy2(/*const*/target); |
215 | /*const*/target.int_member; |
216 | useInt(/*const*/target.int_member); |
217 | useIntConstRef(/*const*/target.int_member); |
218 | useIntPtr(/*const*/target.ptr_member); |
219 | useIntConstPtr(&/*const*/target.int_member); |
220 | |
221 | const S& const_target_ref = /*const*/target; |
222 | const S* const_target_ptr = &/*const*/target; |
223 | S* target_ptr = ⌖ |
224 | |
225 | (void)/*const*/target.at(3); |
226 | ++target.at(3); |
227 | const int civ = /*const*/target.at(3); |
228 | const int& cir = /*const*/target.at(3); |
229 | int& ir = target.at(3); |
230 | target.at(Tag1{}); |
231 | target.weird_overload(); |
232 | } |
233 | )" ); |
234 | } |
235 | |
236 | TEST(ConstReferenceDeclRefExprsTest, RefVar) { |
237 | RunTest<0>(Snippet: R"( |
238 | void f(S& target) { |
239 | useVal(/*const*/target); |
240 | usePtr(&target); |
241 | useConstRef(/*const*/target); |
242 | useConstPtr(&/*const*/target); |
243 | useConstPtrConstRef(&/*const*/target); |
244 | /*const*/target.constMethod(); |
245 | /*const*/target.staticMethod(); |
246 | target.nonConstMethod(); |
247 | /*const*/target(ConstTag{}); |
248 | /*const*/target[42]; |
249 | useConstRef((/*const*/target)); |
250 | (/*const*/target).constMethod(); |
251 | (void)(/*const*/target == /*const*/target); |
252 | (void)/*const*/target; |
253 | (void)&/*const*/target; |
254 | (void)*&/*const*/target; |
255 | /*const*/target; |
256 | S copy1 = /*const*/target; |
257 | S copy2(/*const*/target); |
258 | /*const*/target.int_member; |
259 | useInt(/*const*/target.int_member); |
260 | useIntConstRef(/*const*/target.int_member); |
261 | useIntPtr(/*const*/target.ptr_member); |
262 | useIntConstPtr(&/*const*/target.int_member); |
263 | |
264 | (void)(&/*const*/target)->int_member; |
265 | useIntRef((&target)->int_member); |
266 | |
267 | const S& const_target_ref = /*const*/target; |
268 | const S* const_target_ptr = &/*const*/target; |
269 | S* target_ptr = ⌖ |
270 | |
271 | (void)/*const*/target.at(3); |
272 | ++target.at(3); |
273 | const int civ = /*const*/target.at(3); |
274 | const int& cir = /*const*/target.at(3); |
275 | int& ir = target.at(3); |
276 | target.at(Tag1{}); |
277 | target.weird_overload(); |
278 | } |
279 | )" ); |
280 | } |
281 | |
282 | TEST(ConstReferenceDeclRefExprsTest, PtrVar) { |
283 | RunTest<1>(Snippet: R"( |
284 | void f(S* target) { |
285 | useVal(*/*const*/target); |
286 | usePtr(target); |
287 | useConstRef(*/*const*/target); |
288 | useConstPtr(/*const*/target); |
289 | useConstPtrConstRef(/*const*/target); |
290 | usePtrConstPtr(&target); |
291 | /*const*/target->constMethod(); |
292 | /*const*/target->staticMethod(); |
293 | target->nonConstMethod(); |
294 | (*/*const*/target)(ConstTag{}); |
295 | (*/*const*/target)[42]; |
296 | /*const*/target->operator[](42); |
297 | useConstRef((*/*const*/target)); |
298 | (/*const*/target)->constMethod(); |
299 | (void)(*/*const*/target == */*const*/target); |
300 | (void)*/*const*/target; |
301 | (void)/*const*/target; |
302 | /*const*/target; |
303 | S copy1 = */*const*/target; |
304 | S copy2(*/*const*/target); |
305 | /*const*/target->int_member; |
306 | useInt(/*const*/target->int_member); |
307 | useIntConstRef(/*const*/target->int_member); |
308 | useIntPtr(/*const*/target->ptr_member); |
309 | useIntConstPtr(&/*const*/target->int_member); |
310 | |
311 | const S& const_target_ref = */*const*/target; |
312 | const S* const_target_ptr = /*const*/target; |
313 | S* target_ptr = target; // FIXME: we could chect const usage of `target_ptr` |
314 | |
315 | (void)/*const*/target->at(3); |
316 | ++target->at(3); |
317 | const int civ = /*const*/target->at(3); |
318 | const int& cir = /*const*/target->at(3); |
319 | int& ir = target->at(3); |
320 | target->at(Tag1{}); |
321 | target->weird_overload(); |
322 | } |
323 | )" ); |
324 | } |
325 | |
326 | TEST(ConstReferenceDeclRefExprsTest, ConstPtrVar) { |
327 | RunTest<1>(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 | /*const*/target->constMethod(); |
337 | /*const*/target->staticMethod(); |
338 | (*/*const*/target)(ConstTag{}); |
339 | (*/*const*/target)[42]; |
340 | /*const*/target->operator[](42); |
341 | (void)(*/*const*/target == */*const*/target); |
342 | (void)/*const*/target; |
343 | (void)*/*const*/target; |
344 | /*const*/target; |
345 | if(/*const*/target) {} |
346 | S copy1 = */*const*/target; |
347 | S copy2(*/*const*/target); |
348 | /*const*/target->int_member; |
349 | useInt(/*const*/target->int_member); |
350 | useIntConstRef(/*const*/target->int_member); |
351 | useIntPtr(/*const*/target->ptr_member); |
352 | useIntConstPtr(&/*const*/target->int_member); |
353 | |
354 | const S& const_target_ref = */*const*/target; |
355 | const S* const_target_ptr = /*const*/target; |
356 | |
357 | (void)/*const*/target->at(3); |
358 | const int civ = /*const*/target->at(3); |
359 | const int& cir = /*const*/target->at(3); |
360 | } |
361 | )" ); |
362 | } |
363 | |
364 | TEST(ConstReferenceDeclRefExprsTest, ConstPtrPtrVar) { |
365 | RunTest<2>(Snippet: R"( |
366 | void f(const S** target) { |
367 | useVal(**/*const*/target); |
368 | useConstRef(**/*const*/target); |
369 | useConstPtr(*/*const*/target); |
370 | useConstPtrRef(*/*const*/target); |
371 | useConstPtrPtr(/*const*/target); |
372 | useConstPtrConstPtr(/*const*/target); |
373 | useConstPtrConstRef(*/*const*/target); |
374 | (void)/*const*/target; |
375 | (void)*/*const*/target; |
376 | (void)**/*const*/target; |
377 | /*const*/target; |
378 | if(/*const*/target) {} |
379 | if(*/*const*/target) {} |
380 | S copy1 = **/*const*/target; |
381 | S copy2(**/*const*/target); |
382 | (*/*const*/target)->int_member; |
383 | useInt((*/*const*/target)->int_member); |
384 | useIntConstRef((*/*const*/target)->int_member); |
385 | useIntPtr((*/*const*/target)->ptr_member); |
386 | useIntConstPtr(&(*/*const*/target)->int_member); |
387 | |
388 | const S& const_target_ref = **/*const*/target; |
389 | const S* const_target_ptr = */*const*/target; |
390 | } |
391 | )" ); |
392 | } |
393 | |
394 | TEST(ConstReferenceDeclRefExprsTest, ConstPtrConstPtrVar) { |
395 | RunTest<2>(Snippet: R"( |
396 | void f(const S* const* target) { |
397 | useVal(**/*const*/target); |
398 | useConstRef(**/*const*/target); |
399 | useConstPtr(*/*const*/target); |
400 | useConstPtrConstPtr(/*const*/target); |
401 | useConstPtrConstRef(*/*const*/target); |
402 | (void)/*const*/target; |
403 | (void)*/*const*/target; |
404 | (void)**/*const*/target; |
405 | /*const*/target; |
406 | if(/*const*/target) {} |
407 | if(*/*const*/target) {} |
408 | S copy1 = **/*const*/target; |
409 | S copy2(**/*const*/target); |
410 | (*/*const*/target)->int_member; |
411 | useInt((*/*const*/target)->int_member); |
412 | useIntConstRef((*/*const*/target)->int_member); |
413 | useIntPtr((*/*const*/target)->ptr_member); |
414 | useIntConstPtr(&(*/*const*/target)->int_member); |
415 | |
416 | const S& const_target_ref = **/*const*/target; |
417 | const S* const_target_ptr = */*const*/target; |
418 | } |
419 | )" ); |
420 | } |
421 | |
422 | } // namespace test |
423 | } // namespace tidy |
424 | } // namespace clang |
425 | |