1 | //===---------- ExprMutationAnalyzerTest.cpp ------------------------------===// |
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 | #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" |
10 | #include "clang/AST/TypeLoc.h" |
11 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
12 | #include "clang/ASTMatchers/ASTMatchers.h" |
13 | #include "clang/Tooling/Tooling.h" |
14 | #include "llvm/ADT/SmallString.h" |
15 | #include "gmock/gmock.h" |
16 | #include "gtest/gtest.h" |
17 | #include <cctype> |
18 | |
19 | namespace clang { |
20 | |
21 | using namespace clang::ast_matchers; |
22 | using ::testing::ElementsAre; |
23 | using ::testing::ResultOf; |
24 | using ::testing::Values; |
25 | |
26 | namespace { |
27 | |
28 | using ExprMatcher = internal::Matcher<Expr>; |
29 | using StmtMatcher = internal::Matcher<Stmt>; |
30 | |
31 | std::unique_ptr<ASTUnit> |
32 | buildASTFromCodeWithArgs(const Twine &Code, |
33 | const std::vector<std::string> &Args) { |
34 | SmallString<1024> CodeStorage; |
35 | auto AST = |
36 | tooling::buildASTFromCodeWithArgs(Code: Code.toStringRef(Out&: CodeStorage), Args); |
37 | EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); |
38 | return AST; |
39 | } |
40 | |
41 | std::unique_ptr<ASTUnit> buildASTFromCode(const Twine &Code) { |
42 | return buildASTFromCodeWithArgs(Code, Args: {}); |
43 | } |
44 | |
45 | ExprMatcher declRefTo(StringRef Name) { |
46 | return declRefExpr(to(InnerMatcher: namedDecl(hasName(Name)))); |
47 | } |
48 | |
49 | StmtMatcher withEnclosingCompound(ExprMatcher Matcher) { |
50 | return expr(Matcher, hasAncestor(compoundStmt().bind(ID: "stmt" ))).bind(ID: "expr" ); |
51 | } |
52 | |
53 | bool isMutated(const SmallVectorImpl<BoundNodes> &Results, ASTUnit *AST) { |
54 | const auto *const S = selectFirst<Stmt>(BoundTo: "stmt" , Results); |
55 | const auto *const E = selectFirst<Expr>(BoundTo: "expr" , Results); |
56 | TraversalKindScope RAII(AST->getASTContext(), TK_AsIs); |
57 | return ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(Exp: E); |
58 | } |
59 | |
60 | SmallVector<std::string, 1> |
61 | mutatedBy(const SmallVectorImpl<BoundNodes> &Results, ASTUnit *AST) { |
62 | const auto *const S = selectFirst<Stmt>(BoundTo: "stmt" , Results); |
63 | SmallVector<std::string, 1> Chain; |
64 | ExprMutationAnalyzer Analyzer(*S, AST->getASTContext()); |
65 | |
66 | for (const auto *E = selectFirst<Expr>(BoundTo: "expr" , Results); E != nullptr;) { |
67 | const Stmt *By = Analyzer.findMutation(Exp: E); |
68 | if (!By) |
69 | break; |
70 | |
71 | std::string Buffer; |
72 | llvm::raw_string_ostream Stream(Buffer); |
73 | By->printPretty(OS&: Stream, Helper: nullptr, Policy: AST->getASTContext().getPrintingPolicy()); |
74 | Chain.emplace_back(Args: StringRef(Stream.str()).trim().str()); |
75 | E = dyn_cast<DeclRefExpr>(Val: By); |
76 | } |
77 | return Chain; |
78 | } |
79 | |
80 | std::string removeSpace(std::string s) { |
81 | s.erase(first: std::remove_if(first: s.begin(), last: s.end(), |
82 | pred: [](char c) { return llvm::isSpace(C: c); }), |
83 | last: s.end()); |
84 | return s; |
85 | } |
86 | |
87 | const std::string StdRemoveReference = |
88 | "namespace std {" |
89 | "template<class T> struct remove_reference { typedef T type; };" |
90 | "template<class T> struct remove_reference<T&> { typedef T type; };" |
91 | "template<class T> struct remove_reference<T&&> { typedef T type; }; }" ; |
92 | |
93 | const std::string StdMove = |
94 | "namespace std {" |
95 | "template<class T> typename remove_reference<T>::type&& " |
96 | "move(T&& t) noexcept {" |
97 | "return static_cast<typename remove_reference<T>::type&&>(t); } }" ; |
98 | |
99 | const std::string StdForward = |
100 | "namespace std {" |
101 | "template<class T> T&& " |
102 | "forward(typename remove_reference<T>::type& t) noexcept { return t; }" |
103 | "template<class T> T&& " |
104 | "forward(typename remove_reference<T>::type&& t) noexcept { return t; } }" ; |
105 | |
106 | } // namespace |
107 | |
108 | TEST(ExprMutationAnalyzerTest, Trivial) { |
109 | const auto AST = buildASTFromCode(Code: "void f() { int x; x; }" ); |
110 | const auto Results = |
111 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
112 | EXPECT_FALSE(isMutated(Results, AST.get())); |
113 | } |
114 | |
115 | class AssignmentTest : public ::testing::TestWithParam<std::string> {}; |
116 | |
117 | // This test is for the most basic and direct modification of a variable, |
118 | // assignment to it (e.g. `x = 10;`). |
119 | // It additionally tests that references to a variable are not only captured |
120 | // directly but expressions that result in the variable are handled, too. |
121 | // This includes the comma operator, parens and the ternary operator. |
122 | TEST_P(AssignmentTest, AssignmentModifies) { |
123 | // Test the detection of the raw expression modifications. |
124 | { |
125 | const std::string ModExpr = "x " + GetParam() + " 10" ; |
126 | const auto AST = buildASTFromCode(Code: "void f() { int x; " + ModExpr + "; }" ); |
127 | const auto Results = |
128 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
129 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); |
130 | } |
131 | |
132 | // Test the detection if the expression is surrounded by parens. |
133 | { |
134 | const std::string ModExpr = "(x) " + GetParam() + " 10" ; |
135 | const auto AST = buildASTFromCode(Code: "void f() { int x; " + ModExpr + "; }" ); |
136 | const auto Results = |
137 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
138 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); |
139 | } |
140 | |
141 | // Test the detection if the comma operator yields the expression as result. |
142 | { |
143 | const std::string ModExpr = "x " + GetParam() + " 10" ; |
144 | const auto AST = buildASTFromCodeWithArgs( |
145 | Code: "void f() { int x, y; y, " + ModExpr + "; }" , Args: {"-Wno-unused-value" }); |
146 | const auto Results = |
147 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
148 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); |
149 | } |
150 | |
151 | // Ensure no detection if the comma operator does not yield the expression as |
152 | // result. |
153 | { |
154 | const std::string ModExpr = "y, x, y " + GetParam() + " 10" ; |
155 | const auto AST = buildASTFromCodeWithArgs( |
156 | Code: "void f() { int x, y; " + ModExpr + "; }" , Args: {"-Wno-unused-value" }); |
157 | const auto Results = |
158 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
159 | EXPECT_FALSE(isMutated(Results, AST.get())); |
160 | } |
161 | |
162 | // Test the detection if the a ternary operator can result in the expression. |
163 | { |
164 | const std::string ModExpr = "(y != 0 ? y : x) " + GetParam() + " 10" ; |
165 | const auto AST = |
166 | buildASTFromCode(Code: "void f() { int y = 0, x; " + ModExpr + "; }" ); |
167 | const auto Results = |
168 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
169 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); |
170 | } |
171 | |
172 | // Test the detection if the a ternary operator can result in the expression |
173 | // through multiple nesting of ternary operators. |
174 | { |
175 | const std::string ModExpr = |
176 | "(y != 0 ? (y > 5 ? y : x) : (y)) " + GetParam() + " 10" ; |
177 | const auto AST = |
178 | buildASTFromCode(Code: "void f() { int y = 0, x; " + ModExpr + "; }" ); |
179 | const auto Results = |
180 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
181 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); |
182 | } |
183 | |
184 | // Test the detection if the a ternary operator can result in the expression |
185 | // with additional parens. |
186 | { |
187 | const std::string ModExpr = "(y != 0 ? (y) : ((x))) " + GetParam() + " 10" ; |
188 | const auto AST = |
189 | buildASTFromCode(Code: "void f() { int y = 0, x; " + ModExpr + "; }" ); |
190 | const auto Results = |
191 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
192 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); |
193 | } |
194 | |
195 | // Test the detection for the binary conditional operator. |
196 | { |
197 | const std::string ModExpr = "(y ?: x) " + GetParam() + " 10" ; |
198 | const auto AST = |
199 | buildASTFromCode(Code: "void f() { int y = 0, x; " + ModExpr + "; }" ); |
200 | const auto Results = |
201 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
202 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); |
203 | } |
204 | } |
205 | |
206 | INSTANTIATE_TEST_SUITE_P(AllAssignmentOperators, AssignmentTest, |
207 | Values("=" , "+=" , "-=" , "*=" , "/=" , "%=" , "&=" , "|=" , |
208 | "^=" , "<<=" , ">>=" ) ); |
209 | |
210 | TEST(ExprMutationAnalyzerTest, AssignmentConditionalWithInheritance) { |
211 | const auto AST = buildASTFromCode(Code: "struct Base {void nonconst(); };" |
212 | "struct Derived : Base {};" |
213 | "static void f() {" |
214 | " Derived x, y;" |
215 | " Base &b = true ? x : y;" |
216 | " b.nonconst();" |
217 | "}" ); |
218 | const auto Results = |
219 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
220 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("b" , "b.nonconst()" )); |
221 | } |
222 | |
223 | class IncDecTest : public ::testing::TestWithParam<std::string> {}; |
224 | |
225 | TEST_P(IncDecTest, IncDecModifies) { |
226 | const std::string ModExpr = GetParam(); |
227 | const auto AST = buildASTFromCode(Code: "void f() { int x; " + ModExpr + "; }" ); |
228 | const auto Results = |
229 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
230 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); |
231 | } |
232 | |
233 | INSTANTIATE_TEST_SUITE_P(AllIncDecOperators, IncDecTest, |
234 | Values("++x" , "--x" , "x++" , "x--" , "++(x)" , "--(x)" , |
235 | "(x)++" , "(x)--" ) ); |
236 | |
237 | // Section: member functions |
238 | |
239 | TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) { |
240 | const auto AST = buildASTFromCode( |
241 | Code: "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }" ); |
242 | const auto Results = |
243 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
244 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()" )); |
245 | } |
246 | |
247 | TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) { |
248 | auto AST = buildASTFromCodeWithArgs( |
249 | Code: "struct X { template <class T> void mf(); };" |
250 | "template <class T> void f() { X x; x.mf<T>(); }" , |
251 | Args: {"-fno-delayed-template-parsing" }); |
252 | auto Results = |
253 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
254 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf<T>()" )); |
255 | |
256 | AST = buildASTFromCodeWithArgs(Code: "template <class T> void f() { T x; x.mf(); }" , |
257 | Args: {"-fno-delayed-template-parsing" }); |
258 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
259 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()" )); |
260 | |
261 | AST = buildASTFromCodeWithArgs( |
262 | Code: "template <class T> struct X;" |
263 | "template <class T> void f() { X<T> x; x.mf(); }" , |
264 | Args: {"-fno-delayed-template-parsing" }); |
265 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
266 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()" )); |
267 | } |
268 | |
269 | TEST(ExprMutationAnalyzerTest, ConstMemberFunc) { |
270 | const auto AST = buildASTFromCode( |
271 | Code: "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }" ); |
272 | const auto Results = |
273 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
274 | EXPECT_FALSE(isMutated(Results, AST.get())); |
275 | } |
276 | |
277 | TEST(ExprMutationAnalyzerTest, TypeDependentMemberCall) { |
278 | const auto AST = buildASTFromCodeWithArgs( |
279 | Code: "template <class T> class vector { void push_back(T); }; " |
280 | "template <class T> void f() { vector<T> x; x.push_back(T()); }" , |
281 | Args: {"-fno-delayed-template-parsing" }); |
282 | const auto Results = |
283 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
284 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.push_back(T())" )); |
285 | } |
286 | |
287 | TEST(ExprMutationAnalyzerTest, MemberPointerMemberCall) { |
288 | { |
289 | const auto AST = |
290 | buildASTFromCode(Code: "struct X {};" |
291 | "using T = int (X::*)();" |
292 | "void f(X &x, T m) { X &ref = x; (ref.*m)(); }" ); |
293 | const auto Results = |
294 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "ref" )), Context&: AST->getASTContext()); |
295 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(ref .* m)()" )); |
296 | } |
297 | { |
298 | const auto AST = |
299 | buildASTFromCode(Code: "struct X {};" |
300 | "using T = int (X::*)();" |
301 | "void f(X &x, T const m) { X &ref = x; (ref.*m)(); }" ); |
302 | const auto Results = |
303 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "ref" )), Context&: AST->getASTContext()); |
304 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(ref .* m)()" )); |
305 | } |
306 | { |
307 | const auto AST = |
308 | buildASTFromCode(Code: "struct X {};" |
309 | "using T = int (X::*)() const;" |
310 | "void f(X &x, T m) { X &ref = x; (ref.*m)(); }" ); |
311 | const auto Results = |
312 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "ref" )), Context&: AST->getASTContext()); |
313 | EXPECT_FALSE(isMutated(Results, AST.get())); |
314 | } |
315 | } |
316 | |
317 | // Section: overloaded operators |
318 | |
319 | TEST(ExprMutationAnalyzerTest, NonConstOperator) { |
320 | const auto AST = buildASTFromCode( |
321 | Code: "void f() { struct Foo { Foo& operator=(int); }; Foo x; x = 10; }" ); |
322 | const auto Results = |
323 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
324 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x = 10" )); |
325 | } |
326 | |
327 | TEST(ExprMutationAnalyzerTest, ConstOperator) { |
328 | const auto AST = buildASTFromCode( |
329 | Code: "void f() { struct Foo { int operator()() const; }; Foo x; x(); }" ); |
330 | const auto Results = |
331 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
332 | EXPECT_FALSE(isMutated(Results, AST.get())); |
333 | } |
334 | |
335 | TEST(ExprMutationAnalyzerTest, UnresolvedOperator) { |
336 | const auto AST = buildASTFromCodeWithArgs( |
337 | Code: "template <typename Stream> void input_operator_template() {" |
338 | "Stream x; unsigned y = 42;" |
339 | "x >> y; }" , |
340 | Args: {"-fno-delayed-template-parsing" }); |
341 | const auto Results = |
342 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
343 | EXPECT_TRUE(isMutated(Results, AST.get())); |
344 | } |
345 | |
346 | TEST(ExprMutationAnalyzerTest, DependentOperatorWithNonDependentOperand) { |
347 | // gh57297 |
348 | // The expression to check may not be the dependent operand in a dependent |
349 | // operator. |
350 | |
351 | // Explicitly not declaring a (templated) stream operator |
352 | // so the `<<` is a `binaryOperator` with a dependent type. |
353 | const auto AST = buildASTFromCodeWithArgs( |
354 | Code: "struct Stream { };" |
355 | "template <typename T> void f() { T t; Stream x; x << t; }" , |
356 | Args: {"-fno-delayed-template-parsing" }); |
357 | const auto Results = |
358 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
359 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x << t" )); |
360 | } |
361 | |
362 | TEST(ExprMutationAnalyzerTest, FoldExpression) { |
363 | // gh70323 |
364 | // A fold expression may contain `Exp` as it's initializer. |
365 | // We don't know if the operator modifies `Exp` because the |
366 | // operator is type dependent due to the parameter pack. |
367 | auto AST = buildASTFromCodeWithArgs( |
368 | Code: "struct Stream {};" |
369 | "template <typename... Args> void concatenate(Args... args) " |
370 | "{ Stream x; (x << ... << args); }" , |
371 | Args: {"-fno-delayed-template-parsing" }); |
372 | auto Results = |
373 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
374 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(x << ... << args)" )); |
375 | |
376 | AST = buildASTFromCodeWithArgs( |
377 | Code: "struct Stream {};" |
378 | "template <typename... Args> void concatenate(Args... args) " |
379 | "{ Stream x; (args << ... << x); }" , |
380 | Args: {"-fno-delayed-template-parsing" }); |
381 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
382 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(args << ... << x)" )); |
383 | |
384 | AST = buildASTFromCodeWithArgs( |
385 | Code: "struct Stream {};" |
386 | "template <typename... Args> void concatenate(Args... args) " |
387 | "{ Stream x; (..., (x << args)); }" , |
388 | Args: {"-fno-delayed-template-parsing" }); |
389 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
390 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x << args" )); |
391 | } |
392 | |
393 | // Section: expression as call argument |
394 | |
395 | TEST(ExprMutationAnalyzerTest, ByValueArgument) { |
396 | auto AST = buildASTFromCode(Code: "void g(int); void f() { int x; g(x); }" ); |
397 | auto Results = |
398 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
399 | EXPECT_FALSE(isMutated(Results, AST.get())); |
400 | |
401 | AST = buildASTFromCode(Code: "void g(int*); void f() { int* x; g(x); }" ); |
402 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
403 | EXPECT_FALSE(isMutated(Results, AST.get())); |
404 | |
405 | AST = buildASTFromCode(Code: "typedef int* IntPtr;" |
406 | "void g(IntPtr); void f() { int* x; g(x); }" ); |
407 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
408 | EXPECT_FALSE(isMutated(Results, AST.get())); |
409 | |
410 | AST = buildASTFromCode( |
411 | Code: "struct A {}; A operator+(A, int); void f() { A x; x + 1; }" ); |
412 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
413 | EXPECT_FALSE(isMutated(Results, AST.get())); |
414 | |
415 | AST = buildASTFromCode(Code: "void f() { struct A { A(int); }; int x; A y(x); }" ); |
416 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
417 | EXPECT_FALSE(isMutated(Results, AST.get())); |
418 | |
419 | AST = buildASTFromCode(Code: "struct A { A(); A& operator=(A); };" |
420 | "void f() { A x, y; y = x; }" ); |
421 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
422 | EXPECT_FALSE(isMutated(Results, AST.get())); |
423 | |
424 | AST = buildASTFromCode( |
425 | Code: "template <int> struct A { A(); A(const A&); static void mf(A) {} };" |
426 | "void f() { A<0> x; A<0>::mf(x); }" ); |
427 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
428 | EXPECT_FALSE(isMutated(Results, AST.get())); |
429 | } |
430 | |
431 | TEST(ExprMutationAnalyzerTest, ByConstValueArgument) { |
432 | auto AST = buildASTFromCode(Code: "void g(const int); void f() { int x; g(x); }" ); |
433 | auto Results = |
434 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
435 | EXPECT_FALSE(isMutated(Results, AST.get())); |
436 | |
437 | AST = buildASTFromCode(Code: "void g(int* const); void f() { int* x; g(x); }" ); |
438 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
439 | EXPECT_FALSE(isMutated(Results, AST.get())); |
440 | |
441 | AST = buildASTFromCode(Code: "typedef int* const CIntPtr;" |
442 | "void g(CIntPtr); void f() { int* x; g(x); }" ); |
443 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
444 | EXPECT_FALSE(isMutated(Results, AST.get())); |
445 | |
446 | AST = buildASTFromCode( |
447 | Code: "struct A {}; A operator+(const A, int); void f() { A x; x + 1; }" ); |
448 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
449 | EXPECT_FALSE(isMutated(Results, AST.get())); |
450 | |
451 | AST = buildASTFromCode( |
452 | Code: "void f() { struct A { A(const int); }; int x; A y(x); }" ); |
453 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
454 | EXPECT_FALSE(isMutated(Results, AST.get())); |
455 | |
456 | AST = buildASTFromCode(Code: "template <int> struct A { A(); A(const A&);" |
457 | "static void mf(const A&) {} };" |
458 | "void f() { A<0> x; A<0>::mf(x); }" ); |
459 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
460 | EXPECT_FALSE(isMutated(Results, AST.get())); |
461 | } |
462 | |
463 | TEST(ExprMutationAnalyzerTest, ByNonConstRefArgument) { |
464 | auto AST = buildASTFromCode(Code: "void g(int&); void f() { int x; g(x); }" ); |
465 | auto Results = |
466 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
467 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)" )); |
468 | |
469 | AST = buildASTFromCode(Code: "typedef int& IntRef;" |
470 | "void g(IntRef); void f() { int x; g(x); }" ); |
471 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
472 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)" )); |
473 | |
474 | AST = buildASTFromCode(Code: "template <class T> using TRef = T&;" |
475 | "void g(TRef<int>); void f() { int x; g(x); }" ); |
476 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
477 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)" )); |
478 | |
479 | AST = buildASTFromCode( |
480 | Code: "template <class T> struct identity { using type = T; };" |
481 | "template <class T, class U = T&> void g(typename identity<U>::type);" |
482 | "void f() { int x; g<int>(x); }" ); |
483 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
484 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g<int>(x)" )); |
485 | |
486 | AST = buildASTFromCode(Code: "typedef int* IntPtr;" |
487 | "void g(IntPtr&); void f() { int* x; g(x); }" ); |
488 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
489 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)" )); |
490 | |
491 | AST = buildASTFromCode(Code: "typedef int* IntPtr; typedef IntPtr& IntPtrRef;" |
492 | "void g(IntPtrRef); void f() { int* x; g(x); }" ); |
493 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
494 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)" )); |
495 | |
496 | AST = buildASTFromCode( |
497 | Code: "struct A {}; A operator+(A&, int); void f() { A x; x + 1; }" ); |
498 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
499 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x + 1" )); |
500 | |
501 | AST = buildASTFromCode(Code: "void f() { struct A { A(int&); }; int x; A y(x); }" ); |
502 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
503 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x" )); |
504 | |
505 | AST = buildASTFromCode(Code: "void f() { struct A { A(); A(A&); }; A x; A y(x); }" ); |
506 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
507 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x" )); |
508 | |
509 | AST = buildASTFromCode( |
510 | Code: "template <int> struct A { A(); A(const A&); static void mf(A&) {} };" |
511 | "void f() { A<0> x; A<0>::mf(x); }" ); |
512 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
513 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("A<0>::mf(x)" )); |
514 | } |
515 | |
516 | TEST(ExprMutationAnalyzerTest, ByNonConstRefArgumentFunctionTypeDependent) { |
517 | auto AST = buildASTFromCodeWithArgs( |
518 | Code: "enum MyEnum { foo, bar };" |
519 | "void tryParser(unsigned& first, MyEnum Type) { first++, (void)Type; }" |
520 | "template <MyEnum Type> void parse() {" |
521 | " auto parser = [](unsigned& first) { first++; tryParser(first, Type); " |
522 | "};" |
523 | " unsigned x = 42;" |
524 | " parser(x);" |
525 | "}" , |
526 | Args: {"-fno-delayed-template-parsing" }); |
527 | auto Results = |
528 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
529 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("parser(x)" )); |
530 | } |
531 | |
532 | TEST(ExprMutationAnalyzerTest, ByConstRefArgument) { |
533 | auto AST = buildASTFromCode(Code: "void g(const int&); void f() { int x; g(x); }" ); |
534 | auto Results = |
535 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
536 | EXPECT_FALSE(isMutated(Results, AST.get())); |
537 | |
538 | AST = buildASTFromCode(Code: "typedef const int& CIntRef;" |
539 | "void g(CIntRef); void f() { int x; g(x); }" ); |
540 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
541 | EXPECT_FALSE(isMutated(Results, AST.get())); |
542 | |
543 | AST = buildASTFromCode(Code: "template <class T> using CTRef = const T&;" |
544 | "void g(CTRef<int>); void f() { int x; g(x); }" ); |
545 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
546 | EXPECT_FALSE(isMutated(Results, AST.get())); |
547 | |
548 | AST = |
549 | buildASTFromCode(Code: "template <class T> struct identity { using type = T; };" |
550 | "template <class T, class U = const T&>" |
551 | "void g(typename identity<U>::type);" |
552 | "void f() { int x; g<int>(x); }" ); |
553 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
554 | EXPECT_FALSE(isMutated(Results, AST.get())); |
555 | |
556 | AST = buildASTFromCode( |
557 | Code: "struct A {}; A operator+(const A&, int); void f() { A x; x + 1; }" ); |
558 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
559 | EXPECT_FALSE(isMutated(Results, AST.get())); |
560 | |
561 | AST = buildASTFromCode( |
562 | Code: "void f() { struct A { A(const int&); }; int x; A y(x); }" ); |
563 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
564 | EXPECT_FALSE(isMutated(Results, AST.get())); |
565 | |
566 | AST = buildASTFromCode( |
567 | Code: "void f() { struct A { A(); A(const A&); }; A x; A y(x); }" ); |
568 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
569 | EXPECT_FALSE(isMutated(Results, AST.get())); |
570 | } |
571 | |
572 | TEST(ExprMutationAnalyzerTest, ByNonConstRRefArgument) { |
573 | auto AST = buildASTFromCode( |
574 | Code: "void g(int&&); void f() { int x; g(static_cast<int &&>(x)); }" ); |
575 | auto Results = |
576 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
577 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
578 | ElementsAre("g(static_cast<int &&>(x))" )); |
579 | |
580 | AST = buildASTFromCode(Code: "struct A {}; A operator+(A&&, int);" |
581 | "void f() { A x; static_cast<A &&>(x) + 1; }" ); |
582 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
583 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
584 | ElementsAre("static_cast<A &&>(x) + 1" )); |
585 | |
586 | AST = buildASTFromCode(Code: "void f() { struct A { A(int&&); }; " |
587 | "int x; A y(static_cast<int &&>(x)); }" ); |
588 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
589 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
590 | ElementsAre("static_cast<int &&>(x)" )); |
591 | |
592 | AST = buildASTFromCode(Code: "void f() { struct A { A(); A(A&&); }; " |
593 | "A x; A y(static_cast<A &&>(x)); }" ); |
594 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
595 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
596 | ElementsAre("static_cast<A &&>(x)" )); |
597 | } |
598 | |
599 | TEST(ExprMutationAnalyzerTest, ByConstRRefArgument) { |
600 | auto AST = buildASTFromCode( |
601 | Code: "void g(const int&&); void f() { int x; g(static_cast<int&&>(x)); }" ); |
602 | auto Results = |
603 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
604 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
605 | ElementsAre("static_cast<int &&>(x)" )); |
606 | |
607 | AST = buildASTFromCode(Code: "struct A {}; A operator+(const A&&, int);" |
608 | "void f() { A x; static_cast<A&&>(x) + 1; }" ); |
609 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
610 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
611 | ElementsAre("static_cast<A &&>(x)" )); |
612 | |
613 | AST = buildASTFromCode(Code: "void f() { struct A { A(const int&&); }; " |
614 | "int x; A y(static_cast<int&&>(x)); }" ); |
615 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
616 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
617 | ElementsAre("static_cast<int &&>(x)" )); |
618 | |
619 | AST = buildASTFromCode(Code: "void f() { struct A { A(); A(const A&&); }; " |
620 | "A x; A y(static_cast<A&&>(x)); }" ); |
621 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
622 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
623 | ElementsAre("static_cast<A &&>(x)" )); |
624 | } |
625 | |
626 | // Section: bindings. |
627 | |
628 | TEST(ExprMutationAnalyzerTest, BindingModifies) { |
629 | const auto AST = |
630 | buildASTFromCode(Code: "struct Point { int x; int y; };" |
631 | "void mod(int&);" |
632 | "void f(Point p) { auto& [x, y] = p; mod(x); }" ); |
633 | const auto Results = |
634 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "p" )), Context&: AST->getASTContext()); |
635 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x" , "mod(x)" )); |
636 | } |
637 | |
638 | TEST(ExprMutationAnalyzerTest, BindingDoesNotModify) { |
639 | const auto AST = buildASTFromCode(Code: "struct Point { int x; int y; };" |
640 | "void f(Point p) { auto& [x, y] = p; x; }" ); |
641 | const auto Results = |
642 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "p" )), Context&: AST->getASTContext()); |
643 | EXPECT_FALSE(isMutated(Results, AST.get())); |
644 | } |
645 | |
646 | // section: explicit std::move and std::forward testing |
647 | |
648 | TEST(ExprMutationAnalyzerTest, Move) { |
649 | auto AST = buildASTFromCode(Code: StdRemoveReference + StdMove + |
650 | "void f() { struct A {}; A x; std::move(x); }" ); |
651 | auto Results = |
652 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
653 | EXPECT_FALSE(isMutated(Results, AST.get())); |
654 | |
655 | AST = buildASTFromCode(Code: StdRemoveReference + StdMove + |
656 | "void f() { struct A {}; A x, y; std::move(x) = y; }" ); |
657 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
658 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x) = y" )); |
659 | |
660 | AST = buildASTFromCode(Code: StdRemoveReference + StdMove + |
661 | "void f() { int x, y; y = std::move(x); }" ); |
662 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
663 | EXPECT_FALSE(isMutated(Results, AST.get())); |
664 | |
665 | AST = |
666 | buildASTFromCode(Code: StdRemoveReference + StdMove + |
667 | "struct S { S(); S(const S&); S& operator=(const S&); };" |
668 | "void f() { S x, y; y = std::move(x); }" ); |
669 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
670 | EXPECT_FALSE(isMutated(Results, AST.get())); |
671 | |
672 | AST = buildASTFromCode(Code: StdRemoveReference + StdMove + |
673 | "struct S { S(); S(S&&); S& operator=(S&&); };" |
674 | "void f() { S x, y; y = std::move(x); }" ); |
675 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
676 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)" )); |
677 | |
678 | AST = buildASTFromCode(Code: StdRemoveReference + StdMove + |
679 | "struct S { S(); S(const S&); S(S&&);" |
680 | "S& operator=(const S&); S& operator=(S&&); };" |
681 | "void f() { S x, y; y = std::move(x); }" ); |
682 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
683 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)" )); |
684 | |
685 | AST = buildASTFromCode(Code: StdRemoveReference + StdMove + |
686 | "struct S { S(); S(const S&); S(S&&);" |
687 | "S& operator=(const S&); S& operator=(S&&); };" |
688 | "void f() { const S x; S y; y = std::move(x); }" ); |
689 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
690 | EXPECT_FALSE(isMutated(Results, AST.get())); |
691 | |
692 | AST = buildASTFromCode(Code: StdRemoveReference + StdMove + |
693 | "struct S { S(); S& operator=(S); };" |
694 | "void f() { S x, y; y = std::move(x); }" ); |
695 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
696 | EXPECT_FALSE(isMutated(Results, AST.get())); |
697 | |
698 | AST = buildASTFromCode(Code: StdRemoveReference + StdMove + |
699 | "struct S{}; void f() { S x, y; y = std::move(x); }" ); |
700 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
701 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)" )); |
702 | |
703 | AST = buildASTFromCode( |
704 | Code: StdRemoveReference + StdMove + |
705 | "struct S{}; void f() { const S x; S y; y = std::move(x); }" ); |
706 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
707 | EXPECT_FALSE(isMutated(Results, AST.get())); |
708 | } |
709 | |
710 | TEST(ExprMutationAnalyzerTest, Forward) { |
711 | auto AST = |
712 | buildASTFromCode(Code: StdRemoveReference + StdForward + |
713 | "void f() { struct A {}; A x; std::forward<A &>(x); }" ); |
714 | auto Results = |
715 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
716 | EXPECT_FALSE(isMutated(Results, AST.get())); |
717 | |
718 | AST = buildASTFromCode( |
719 | Code: StdRemoveReference + StdForward + |
720 | "void f() { struct A {}; A x, y; std::forward<A &>(x) = y; }" ); |
721 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
722 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
723 | ElementsAre("std::forward<A &>(x) = y" )); |
724 | } |
725 | |
726 | // section: template constellations that prohibit reasoning about modifications |
727 | // as it depends on instantiations. |
728 | |
729 | TEST(ExprMutationAnalyzerTest, CallUnresolved) { |
730 | auto AST = |
731 | buildASTFromCodeWithArgs(Code: "template <class T> void f() { T x; g(x); }" , |
732 | Args: {"-fno-delayed-template-parsing" }); |
733 | auto Results = |
734 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
735 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)" )); |
736 | |
737 | AST = |
738 | buildASTFromCodeWithArgs(Code: "template <int N> void f() { char x[N]; g(x); }" , |
739 | Args: {"-fno-delayed-template-parsing" }); |
740 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
741 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)" )); |
742 | |
743 | AST = buildASTFromCodeWithArgs( |
744 | Code: "template <class T> void f(T t) { int x; g(t, x); }" , |
745 | Args: {"-fno-delayed-template-parsing" }); |
746 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
747 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)" )); |
748 | |
749 | AST = buildASTFromCodeWithArgs( |
750 | Code: "template <class T> void f(T t) { int x; t.mf(x); }" , |
751 | Args: {"-fno-delayed-template-parsing" }); |
752 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
753 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)" )); |
754 | |
755 | AST = buildASTFromCodeWithArgs( |
756 | Code: "template <class T> struct S;" |
757 | "template <class T> void f() { S<T> s; int x; s.mf(x); }" , |
758 | Args: {"-fno-delayed-template-parsing" }); |
759 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
760 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)" )); |
761 | |
762 | AST = buildASTFromCodeWithArgs( |
763 | Code: "struct S { template <class T> void mf(); };" |
764 | "template <class T> void f(S s) { int x; s.mf<T>(x); }" , |
765 | Args: {"-fno-delayed-template-parsing" }); |
766 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
767 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf<T>(x)" )); |
768 | |
769 | AST = buildASTFromCodeWithArgs(Code: "template <class F>" |
770 | "void g(F f) { int x; f(x); } " , |
771 | Args: {"-fno-delayed-template-parsing" }); |
772 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
773 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)" )); |
774 | |
775 | AST = buildASTFromCodeWithArgs( |
776 | Code: "template <class T> void f() { int x; (void)T(x); }" , |
777 | Args: {"-fno-delayed-template-parsing" }); |
778 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
779 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)" )); |
780 | } |
781 | |
782 | // section: return values |
783 | |
784 | TEST(ExprMutationAnalyzerTest, ReturnAsValue) { |
785 | auto AST = buildASTFromCode(Code: "int f() { int x; return x; }" ); |
786 | auto Results = |
787 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
788 | EXPECT_FALSE(isMutated(Results, AST.get())); |
789 | |
790 | AST = buildASTFromCode(Code: "int* f() { int* x; return x; }" ); |
791 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
792 | EXPECT_FALSE(isMutated(Results, AST.get())); |
793 | |
794 | AST = buildASTFromCode(Code: "typedef int* IntPtr;" |
795 | "IntPtr f() { int* x; return x; }" ); |
796 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
797 | EXPECT_FALSE(isMutated(Results, AST.get())); |
798 | } |
799 | |
800 | TEST(ExprMutationAnalyzerTest, ReturnAsNonConstRef) { |
801 | const auto AST = buildASTFromCode(Code: "int& f() { int x; return x; }" ); |
802 | const auto Results = |
803 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
804 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("return x;" )); |
805 | } |
806 | |
807 | TEST(ExprMutationAnalyzerTest, ReturnAsConstRef) { |
808 | const auto AST = buildASTFromCode(Code: "const int& f() { int x; return x; }" ); |
809 | const auto Results = |
810 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
811 | EXPECT_FALSE(isMutated(Results, AST.get())); |
812 | } |
813 | |
814 | TEST(ExprMutationAnalyzerTest, ReturnAsNonConstRRef) { |
815 | const auto AST = |
816 | buildASTFromCode(Code: "int&& f() { int x; return static_cast<int &&>(x); }" ); |
817 | const auto Results = |
818 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
819 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
820 | ElementsAre("static_cast<int &&>(x)" )); |
821 | } |
822 | |
823 | TEST(ExprMutationAnalyzerTest, ReturnAsConstRRef) { |
824 | const auto AST = buildASTFromCode( |
825 | Code: "const int&& f() { int x; return static_cast<int&&>(x); }" ); |
826 | const auto Results = |
827 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
828 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
829 | ElementsAre("static_cast<int &&>(x)" )); |
830 | } |
831 | |
832 | // section: taking the address of a variable and pointers |
833 | |
834 | TEST(ExprMutationAnalyzerTest, TakeAddress) { |
835 | const auto AST = buildASTFromCode(Code: "void g(int*); void f() { int x; g(&x); }" ); |
836 | const auto Results = |
837 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
838 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("&x" )); |
839 | } |
840 | |
841 | TEST(ExprMutationAnalyzerTest, ArrayToPointerDecay) { |
842 | const auto AST = |
843 | buildASTFromCode(Code: "void g(int*); void f() { int x[2]; g(x); }" ); |
844 | const auto Results = |
845 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
846 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x" )); |
847 | } |
848 | |
849 | TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) { |
850 | const auto AST = buildASTFromCodeWithArgs( |
851 | Code: "template <typename T> struct S { static constexpr int v = 8; };" |
852 | "template <> struct S<int> { static constexpr int v = 4; };" |
853 | "void g(char*);" |
854 | "template <typename T> void f() { char x[S<T>::v]; g(x); }" |
855 | "template <> void f<int>() { char y[S<int>::v]; g(y); }" , |
856 | Args: {"-fno-delayed-template-parsing" }); |
857 | const auto ResultsX = |
858 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
859 | EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)" )); |
860 | const auto ResultsY = |
861 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "y" )), Context&: AST->getASTContext()); |
862 | EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y" )); |
863 | } |
864 | |
865 | // section: special case: all created references are non-mutating themself |
866 | // and therefore all become 'const'/the value is not modified! |
867 | |
868 | TEST(ExprMutationAnalyzerTest, FollowRefModified) { |
869 | auto AST = buildASTFromCode( |
870 | Code: "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; " |
871 | "int& r3 = r2; r3 = 10; }" ); |
872 | auto Results = |
873 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
874 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
875 | ElementsAre("r0" , "r1" , "r2" , "r3" , "r3 = 10" )); |
876 | |
877 | AST = buildASTFromCode(Code: "typedef int& IntRefX;" |
878 | "using IntRefY = int&;" |
879 | "void f() { int x; IntRefX r0 = x; IntRefY r1 = r0;" |
880 | "decltype((x)) r2 = r1; r2 = 10; }" ); |
881 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
882 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
883 | ElementsAre("r0" , "r1" , "r2" , "r2 = 10" )); |
884 | } |
885 | |
886 | TEST(ExprMutationAnalyzerTest, FollowRefNotModified) { |
887 | auto AST = buildASTFromCode( |
888 | Code: "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; " |
889 | "int& r3 = r2; int& r4 = r3; int& r5 = r4;}" ); |
890 | auto Results = |
891 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
892 | EXPECT_FALSE(isMutated(Results, AST.get())); |
893 | |
894 | AST = buildASTFromCode(Code: "void f() { int x; int& r0 = x; const int& r1 = r0;}" ); |
895 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
896 | EXPECT_FALSE(isMutated(Results, AST.get())); |
897 | |
898 | AST = buildASTFromCode(Code: "typedef const int& CIntRefX;" |
899 | "using CIntRefY = const int&;" |
900 | "void f() { int x; int& r0 = x; CIntRefX r1 = r0;" |
901 | "CIntRefY r2 = r1; decltype((r1)) r3 = r2;}" ); |
902 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
903 | EXPECT_FALSE(isMutated(Results, AST.get())); |
904 | } |
905 | |
906 | TEST(ExprMutationAnalyzerTest, FollowConditionalRefModified) { |
907 | const auto AST = buildASTFromCode( |
908 | Code: "void f() { int x, y; bool b; int &r = b ? x : y; r = 10; }" ); |
909 | const auto Results = |
910 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
911 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("r" , "r = 10" )); |
912 | } |
913 | |
914 | TEST(ExprMutationAnalyzerTest, FollowConditionalRefNotModified) { |
915 | const auto AST = |
916 | buildASTFromCode(Code: "void f() { int x, y; bool b; int& r = b ? x : y; }" ); |
917 | const auto Results = |
918 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
919 | EXPECT_FALSE(isMutated(Results, AST.get())); |
920 | } |
921 | |
922 | TEST(ExprMutationAnalyzerTest, FollowFuncArgModified) { |
923 | auto AST = buildASTFromCode(Code: "template <class T> void g(T&& t) { t = 10; }" |
924 | "void f() { int x; g(x); }" ); |
925 | auto Results = |
926 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
927 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)" )); |
928 | |
929 | AST = buildASTFromCode( |
930 | Code: "void h(int&);" |
931 | "template <class... Args> void g(Args&&... args) { h(args...); }" |
932 | "void f() { int x; g(x); }" ); |
933 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
934 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)" )); |
935 | |
936 | AST = buildASTFromCode( |
937 | Code: "void h(int&, int);" |
938 | "template <class... Args> void g(Args&&... args) { h(args...); }" |
939 | "void f() { int x, y; g(x, y); }" ); |
940 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
941 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x, y)" )); |
942 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "y" )), Context&: AST->getASTContext()); |
943 | EXPECT_FALSE(isMutated(Results, AST.get())); |
944 | |
945 | AST = buildASTFromCode( |
946 | Code: "void h(int, int&);" |
947 | "template <class... Args> void g(Args&&... args) { h(args...); }" |
948 | "void f() { int x, y; g(y, x); }" ); |
949 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
950 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(y, x)" )); |
951 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "y" )), Context&: AST->getASTContext()); |
952 | EXPECT_FALSE(isMutated(Results, AST.get())); |
953 | |
954 | AST = buildASTFromCode(Code: "struct S { template <class T> S(T&& t) { t = 10; } };" |
955 | "void f() { int x; S s(x); }" ); |
956 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
957 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x" )); |
958 | |
959 | AST = buildASTFromCode( |
960 | Code: "struct S { template <class T> S(T&& t) : m(++t) { } int m; };" |
961 | "void f() { int x; S s(x); }" ); |
962 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
963 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x" )); |
964 | |
965 | AST = buildASTFromCode(Code: "template <class U> struct S {" |
966 | "template <class T> S(T&& t) : m(++t) { } U m; };" |
967 | "void f() { int x; S<int> s(x); }" ); |
968 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
969 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x" )); |
970 | |
971 | AST = buildASTFromCode(Code: StdRemoveReference + StdForward + |
972 | "template <class... Args> void u(Args&...);" |
973 | "template <class... Args> void h(Args&&... args)" |
974 | "{ u(std::forward<Args>(args)...); }" |
975 | "template <class... Args> void g(Args&&... args)" |
976 | "{ h(std::forward<Args>(args)...); }" |
977 | "void f() { int x; g(x); }" ); |
978 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
979 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)" )); |
980 | |
981 | AST = buildASTFromCode( |
982 | Code: StdRemoveReference + StdForward + |
983 | "template <class T> void f1(T &&a);" |
984 | "template <class T> void f2(T &&a);" |
985 | "template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); }" |
986 | "template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); }" |
987 | "void f() { int x; f1(x); }" ); |
988 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
989 | EXPECT_FALSE(isMutated(Results, AST.get())); |
990 | |
991 | AST = buildASTFromCode( |
992 | Code: StdRemoveReference + StdForward + |
993 | "template <class T> void f1(T &&a);" |
994 | "template <class T> void f2(T &&a);" |
995 | "template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); }" |
996 | "template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); a++; }" |
997 | "void f() { int x; f1(x); }" ); |
998 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
999 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f1(x)" )); |
1000 | |
1001 | AST = buildASTFromCode( |
1002 | Code: StdRemoveReference + StdForward + |
1003 | "template <class T> void f1(T &&a);" |
1004 | "template <class T> void f2(T &&a);" |
1005 | "template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); a++; }" |
1006 | "template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); }" |
1007 | "void f() { int x; f1(x); }" ); |
1008 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1009 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f1(x)" )); |
1010 | } |
1011 | |
1012 | TEST(ExprMutationAnalyzerTest, FollowFuncArgNotModified) { |
1013 | auto AST = buildASTFromCode(Code: "template <class T> void g(T&&) {}" |
1014 | "void f() { int x; g(x); }" ); |
1015 | auto Results = |
1016 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1017 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1018 | |
1019 | AST = buildASTFromCode(Code: "template <class T> void g(T&& t) { t; }" |
1020 | "void f() { int x; g(x); }" ); |
1021 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1022 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1023 | |
1024 | AST = buildASTFromCode(Code: "template <class... Args> void g(Args&&...) {}" |
1025 | "void f() { int x; g(x); }" ); |
1026 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1027 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1028 | |
1029 | AST = buildASTFromCode(Code: "template <class... Args> void g(Args&&...) {}" |
1030 | "void f() { int y, x; g(y, x); }" ); |
1031 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1032 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1033 | |
1034 | AST = buildASTFromCode( |
1035 | Code: "void h(int, int&);" |
1036 | "template <class... Args> void g(Args&&... args) { h(args...); }" |
1037 | "void f() { int x, y; g(x, y); }" ); |
1038 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1039 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1040 | |
1041 | AST = buildASTFromCode(Code: "struct S { template <class T> S(T&& t) { t; } };" |
1042 | "void f() { int x; S s(x); }" ); |
1043 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1044 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1045 | |
1046 | AST = buildASTFromCode( |
1047 | Code: "struct S { template <class T> S(T&& t) : m(t) { } int m; };" |
1048 | "void f() { int x; S s(x); }" ); |
1049 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1050 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1051 | |
1052 | AST = buildASTFromCode(Code: "template <class U> struct S {" |
1053 | "template <class T> S(T&& t) : m(t) { } U m; };" |
1054 | "void f() { int x; S<int> s(x); }" ); |
1055 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1056 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1057 | |
1058 | AST = buildASTFromCode(Code: StdRemoveReference + StdForward + |
1059 | "template <class... Args> void u(Args...);" |
1060 | "template <class... Args> void h(Args&&... args)" |
1061 | "{ u(std::forward<Args>(args)...); }" |
1062 | "template <class... Args> void g(Args&&... args)" |
1063 | "{ h(std::forward<Args>(args)...); }" |
1064 | "void f() { int x; g(x); }" ); |
1065 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1066 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1067 | } |
1068 | |
1069 | // section: builtin arrays |
1070 | |
1071 | TEST(ExprMutationAnalyzerTest, ArrayElementModified) { |
1072 | const auto AST = buildASTFromCode(Code: "void f() { int x[2]; x[0] = 10; }" ); |
1073 | const auto Results = |
1074 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1075 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x[0] = 10" )); |
1076 | } |
1077 | |
1078 | TEST(ExprMutationAnalyzerTest, ArrayElementNotModified) { |
1079 | const auto AST = buildASTFromCode(Code: "void f() { int x[2]; x[0]; }" ); |
1080 | const auto Results = |
1081 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1082 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1083 | } |
1084 | |
1085 | // section: member modifications |
1086 | |
1087 | TEST(ExprMutationAnalyzerTest, NestedMemberModified) { |
1088 | auto AST = |
1089 | buildASTFromCode(Code: "void f() { struct A { int vi; }; struct B { A va; }; " |
1090 | "struct C { B vb; }; C x; x.vb.va.vi = 10; }" ); |
1091 | auto Results = |
1092 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1093 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.vb.va.vi = 10" )); |
1094 | |
1095 | AST = buildASTFromCodeWithArgs( |
1096 | Code: "template <class T> void f() { T x; x.y.z = 10; }" , |
1097 | Args: {"-fno-delayed-template-parsing" }); |
1098 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1099 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10" )); |
1100 | |
1101 | AST = buildASTFromCodeWithArgs( |
1102 | Code: "template <class T> struct S;" |
1103 | "template <class T> void f() { S<T> x; x.y.z = 10; }" , |
1104 | Args: {"-fno-delayed-template-parsing" }); |
1105 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1106 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10" )); |
1107 | } |
1108 | |
1109 | TEST(ExprMutationAnalyzerTest, NestedMemberNotModified) { |
1110 | auto AST = |
1111 | buildASTFromCode(Code: "void f() { struct A { int vi; }; struct B { A va; }; " |
1112 | "struct C { B vb; }; C x; x.vb.va.vi; }" ); |
1113 | auto Results = |
1114 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1115 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1116 | |
1117 | AST = buildASTFromCodeWithArgs(Code: "template <class T> void f() { T x; x.y.z; }" , |
1118 | Args: {"-fno-delayed-template-parsing" }); |
1119 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1120 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1121 | |
1122 | AST = |
1123 | buildASTFromCodeWithArgs(Code: "template <class T> struct S;" |
1124 | "template <class T> void f() { S<T> x; x.y.z; }" , |
1125 | Args: {"-fno-delayed-template-parsing" }); |
1126 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1127 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1128 | } |
1129 | |
1130 | // section: casts |
1131 | |
1132 | TEST(ExprMutationAnalyzerTest, CastToValue) { |
1133 | const auto AST = |
1134 | buildASTFromCode(Code: "void f() { int x; static_cast<double>(x); }" ); |
1135 | const auto Results = |
1136 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1137 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1138 | } |
1139 | |
1140 | TEST(ExprMutationAnalyzerTest, CastToRefModified) { |
1141 | auto AST = |
1142 | buildASTFromCode(Code: "void f() { int x; static_cast<int &>(x) = 10; }" ); |
1143 | auto Results = |
1144 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1145 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
1146 | ElementsAre("static_cast<int &>(x)" )); |
1147 | |
1148 | AST = buildASTFromCode(Code: "typedef int& IntRef;" |
1149 | "void f() { int x; static_cast<IntRef>(x) = 10; }" ); |
1150 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1151 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
1152 | ElementsAre("static_cast<IntRef>(x)" )); |
1153 | } |
1154 | |
1155 | TEST(ExprMutationAnalyzerTest, CastToRefNotModified) { |
1156 | const auto AST = |
1157 | buildASTFromCode(Code: "void f() { int x; static_cast<int&>(x); }" ); |
1158 | const auto Results = |
1159 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1160 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
1161 | ElementsAre("static_cast<int &>(x)" )); |
1162 | } |
1163 | |
1164 | TEST(ExprMutationAnalyzerTest, CastToConstRef) { |
1165 | auto AST = |
1166 | buildASTFromCode(Code: "void f() { int x; static_cast<const int&>(x); }" ); |
1167 | auto Results = |
1168 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1169 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1170 | |
1171 | AST = buildASTFromCode(Code: "typedef const int& CIntRef;" |
1172 | "void f() { int x; static_cast<CIntRef>(x); }" ); |
1173 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1174 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1175 | } |
1176 | |
1177 | // section: comma expressions |
1178 | |
1179 | TEST(ExprMutationAnalyzerTest, CommaExprWithAnAssigment) { |
1180 | const auto AST = buildASTFromCodeWithArgs( |
1181 | Code: "void f() { int x; int y; (x, y) = 5; }" , Args: {"-Wno-unused-value" }); |
1182 | const auto Results = |
1183 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "y" )), Context&: AST->getASTContext()); |
1184 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1185 | } |
1186 | |
1187 | TEST(ExprMutationAnalyzerTest, CommaExprWithDecOp) { |
1188 | const auto AST = buildASTFromCodeWithArgs( |
1189 | Code: "void f() { int x; int y; (x, y)++; }" , Args: {"-Wno-unused-value" }); |
1190 | const auto Results = |
1191 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "y" )), Context&: AST->getASTContext()); |
1192 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1193 | } |
1194 | |
1195 | TEST(ExprMutationAnalyzerTest, CommaExprWithNonConstMemberCall) { |
1196 | const auto AST = buildASTFromCodeWithArgs( |
1197 | Code: "class A { public: int mem; void f() { mem ++; } };" |
1198 | "void fn() { A o1, o2; (o1, o2).f(); }" , |
1199 | Args: {"-Wno-unused-value" }); |
1200 | const auto Results = |
1201 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "o2" )), Context&: AST->getASTContext()); |
1202 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1203 | } |
1204 | |
1205 | TEST(ExprMutationAnalyzerTest, CommaExprWithConstMemberCall) { |
1206 | const auto AST = buildASTFromCodeWithArgs( |
1207 | Code: "class A { public: int mem; void f() const { } };" |
1208 | "void fn() { A o1, o2; (o1, o2).f(); }" , |
1209 | Args: {"-Wno-unused-value" }); |
1210 | const auto Results = |
1211 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "o2" )), Context&: AST->getASTContext()); |
1212 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1213 | } |
1214 | |
1215 | TEST(ExprMutationAnalyzerTest, CommaExprWithCallExpr) { |
1216 | const auto AST = |
1217 | buildASTFromCodeWithArgs(Code: "class A { public: int mem; void f(A &O1) {} };" |
1218 | "void fn() { A o1, o2; o2.f((o2, o1)); }" , |
1219 | Args: {"-Wno-unused-value" }); |
1220 | const auto Results = |
1221 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "o1" )), Context&: AST->getASTContext()); |
1222 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1223 | } |
1224 | |
1225 | TEST(ExprMutationAnalyzerTest, CommaExprWithCallUnresolved) { |
1226 | auto AST = buildASTFromCodeWithArgs( |
1227 | Code: "template <class T> struct S;" |
1228 | "template <class T> void f() { S<T> s; int x, y; s.mf((y, x)); }" , |
1229 | Args: {"-fno-delayed-template-parsing" , "-Wno-unused-value" }); |
1230 | auto Results = |
1231 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1232 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1233 | |
1234 | AST = buildASTFromCodeWithArgs( |
1235 | Code: "template <class T> void f(T t) { int x, y; g(t, (y, x)); }" , |
1236 | Args: {"-fno-delayed-template-parsing" , "-Wno-unused-value" }); |
1237 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1238 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1239 | } |
1240 | |
1241 | TEST(ExprMutationAnalyzerTest, CommaExprParmRef) { |
1242 | const auto AST = |
1243 | buildASTFromCodeWithArgs(Code: "class A { public: int mem;};" |
1244 | "extern void fn(A &o1);" |
1245 | "void fn2 () { A o1, o2; fn((o2, o1)); } " , |
1246 | Args: {"-Wno-unused-value" }); |
1247 | const auto Results = |
1248 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "o1" )), Context&: AST->getASTContext()); |
1249 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1250 | } |
1251 | |
1252 | TEST(ExprMutationAnalyzerTest, CommaExprWithAmpersandOp) { |
1253 | const auto AST = buildASTFromCodeWithArgs(Code: "class A { public: int mem;};" |
1254 | "void fn () { A o1, o2;" |
1255 | "void *addr = &(o2, o1); } " , |
1256 | Args: {"-Wno-unused-value" }); |
1257 | const auto Results = |
1258 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "o1" )), Context&: AST->getASTContext()); |
1259 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1260 | } |
1261 | |
1262 | TEST(ExprMutationAnalyzerTest, CommaExprAsReturnAsValue) { |
1263 | auto AST = buildASTFromCodeWithArgs(Code: "int f() { int x, y; return (x, y); }" , |
1264 | Args: {"-Wno-unused-value" }); |
1265 | auto Results = |
1266 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "y" )), Context&: AST->getASTContext()); |
1267 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1268 | } |
1269 | |
1270 | TEST(ExprMutationAnalyzerTest, CommaEpxrAsReturnAsNonConstRef) { |
1271 | const auto AST = buildASTFromCodeWithArgs( |
1272 | Code: "int& f() { int x, y; return (y, x); }" , Args: {"-Wno-unused-value" }); |
1273 | const auto Results = |
1274 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1275 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1276 | } |
1277 | |
1278 | TEST(ExprMutationAnalyzerTest, CommaExprAsArrayToPointerDecay) { |
1279 | const auto AST = |
1280 | buildASTFromCodeWithArgs(Code: "void g(int*); " |
1281 | "void f() { int x[2], y[2]; g((y, x)); }" , |
1282 | Args: {"-Wno-unused-value" }); |
1283 | const auto Results = |
1284 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1285 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1286 | } |
1287 | |
1288 | TEST(ExprMutationAnalyzerTest, CommaExprAsUniquePtr) { |
1289 | const std::string UniquePtrDef = "template <class T> struct UniquePtr {" |
1290 | " UniquePtr();" |
1291 | " UniquePtr(const UniquePtr&) = delete;" |
1292 | " T& operator*() const;" |
1293 | " T* operator->() const;" |
1294 | "};" ; |
1295 | const auto AST = buildASTFromCodeWithArgs( |
1296 | Code: UniquePtrDef + "template <class T> void f() " |
1297 | "{ UniquePtr<T> x; UniquePtr<T> y;" |
1298 | " (y, x)->mf(); }" , |
1299 | Args: {"-fno-delayed-template-parsing" , "-Wno-unused-value" }); |
1300 | const auto Results = |
1301 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1302 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1303 | } |
1304 | |
1305 | TEST(ExprMutationAnalyzerTest, CommaNestedConditional) { |
1306 | const std::string Code = "void f() { int x, y = 42;" |
1307 | " y, (true ? x : y) = 42; }" ; |
1308 | const auto AST = buildASTFromCodeWithArgs(Code, Args: {"-Wno-unused-value" }); |
1309 | const auto Results = |
1310 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1311 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
1312 | ElementsAre("(true ? x : y) = 42" )); |
1313 | } |
1314 | |
1315 | // section: lambda captures |
1316 | |
1317 | TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByValue) { |
1318 | const auto AST = buildASTFromCode(Code: "void f() { int x; [=]() { x; }; }" ); |
1319 | const auto Results = |
1320 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1321 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1322 | } |
1323 | |
1324 | TEST(ExprMutationAnalyzerTest, LambdaExplicitCaptureByValue) { |
1325 | const auto AST = buildASTFromCode(Code: "void f() { int x; [x]() { x; }; }" ); |
1326 | const auto Results = |
1327 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1328 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1329 | } |
1330 | |
1331 | TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByRef) { |
1332 | const auto AST = buildASTFromCode(Code: "void f() { int x; [&]() { x = 10; }; }" ); |
1333 | const auto Results = |
1334 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1335 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
1336 | ElementsAre(ResultOf(removeSpace, "[&](){x=10;}" ))); |
1337 | } |
1338 | |
1339 | TEST(ExprMutationAnalyzerTest, LambdaExplicitCaptureByRef) { |
1340 | const auto AST = buildASTFromCode(Code: "void f() { int x; [&x]() { x = 10; }; }" ); |
1341 | const auto Results = |
1342 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1343 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
1344 | ElementsAre(ResultOf(removeSpace, "[&x](){x=10;}" ))); |
1345 | } |
1346 | |
1347 | // section: range-for loops |
1348 | |
1349 | TEST(ExprMutationAnalyzerTest, RangeForArrayByRefModified) { |
1350 | auto AST = |
1351 | buildASTFromCode(Code: "void f() { int x[2]; for (int& e : x) e = 10; }" ); |
1352 | auto Results = |
1353 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1354 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
1355 | ElementsAre("for (int &e : x)\n e = 10;" )); |
1356 | |
1357 | AST = buildASTFromCode(Code: "typedef int& IntRef;" |
1358 | "void f() { int x[2]; for (IntRef e : x) e = 10; }" ); |
1359 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1360 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
1361 | ElementsAre("for (IntRef e : x)\n e = 10;" )); |
1362 | } |
1363 | |
1364 | TEST(ExprMutationAnalyzerTest, RangeForArrayByRefModifiedByImplicitInit) { |
1365 | const auto AST = |
1366 | buildASTFromCode(Code: "void f() { int x[2]; for (int& e : x) e; }" ); |
1367 | const auto Results = |
1368 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1369 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1370 | } |
1371 | |
1372 | TEST(ExprMutationAnalyzerTest, RangeForArrayByValue) { |
1373 | auto AST = buildASTFromCode(Code: "void f() { int x[2]; for (int e : x) e = 10; }" ); |
1374 | auto Results = |
1375 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1376 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1377 | |
1378 | AST = |
1379 | buildASTFromCode(Code: "void f() { int* x[2]; for (int* e : x) e = nullptr; }" ); |
1380 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1381 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1382 | |
1383 | AST = buildASTFromCode( |
1384 | Code: "typedef int* IntPtr;" |
1385 | "void f() { int* x[2]; for (IntPtr e : x) e = nullptr; }" ); |
1386 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1387 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1388 | } |
1389 | |
1390 | TEST(ExprMutationAnalyzerTest, RangeForArrayByConstRef) { |
1391 | auto AST = |
1392 | buildASTFromCode(Code: "void f() { int x[2]; for (const int& e : x) e; }" ); |
1393 | auto Results = |
1394 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1395 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1396 | |
1397 | AST = buildASTFromCode(Code: "typedef const int& CIntRef;" |
1398 | "void f() { int x[2]; for (CIntRef e : x) e; }" ); |
1399 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1400 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1401 | } |
1402 | |
1403 | TEST(ExprMutationAnalyzerTest, RangeForNonArrayByRefModified) { |
1404 | const auto AST = |
1405 | buildASTFromCode(Code: "struct V { int* begin(); int* end(); };" |
1406 | "void f() { V x; for (int& e : x) e = 10; }" ); |
1407 | const auto Results = |
1408 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1409 | EXPECT_THAT(mutatedBy(Results, AST.get()), |
1410 | ElementsAre("for (int &e : x)\n e = 10;" )); |
1411 | } |
1412 | |
1413 | TEST(ExprMutationAnalyzerTest, RangeForNonArrayByRefNotModified) { |
1414 | const auto AST = buildASTFromCode(Code: "struct V { int* begin(); int* end(); };" |
1415 | "void f() { V x; for (int& e : x) e; }" ); |
1416 | const auto Results = |
1417 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1418 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1419 | } |
1420 | |
1421 | TEST(ExprMutationAnalyzerTest, RangeForNonArrayByValue) { |
1422 | const auto AST = buildASTFromCode( |
1423 | Code: "struct V { const int* begin() const; const int* end() const; };" |
1424 | "void f() { V x; for (int e : x) e; }" ); |
1425 | const auto Results = |
1426 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1427 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1428 | } |
1429 | |
1430 | TEST(ExprMutationAnalyzerTest, RangeForNonArrayByConstRef) { |
1431 | const auto AST = buildASTFromCode( |
1432 | Code: "struct V { const int* begin() const; const int* end() const; };" |
1433 | "void f() { V x; for (const int& e : x) e; }" ); |
1434 | const auto Results = |
1435 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1436 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1437 | } |
1438 | |
1439 | // section: unevaluated expressions |
1440 | |
1441 | TEST(ExprMutationAnalyzerTest, UnevaluatedExpressions) { |
1442 | auto AST = buildASTFromCode(Code: "void f() { int x, y; decltype(x = 10) z = y; }" ); |
1443 | auto Results = |
1444 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1445 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1446 | |
1447 | AST = buildASTFromCode(Code: "void f() { int x, y; __typeof(x = 10) z = y; }" ); |
1448 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1449 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1450 | |
1451 | AST = buildASTFromCode(Code: "void f() { int x, y; __typeof__(x = 10) z = y; }" ); |
1452 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1453 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1454 | |
1455 | AST = buildASTFromCode(Code: "void f() { int x; sizeof(x = 10); }" ); |
1456 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1457 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1458 | |
1459 | AST = buildASTFromCode(Code: "void f() { int x; alignof(x = 10); }" ); |
1460 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1461 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1462 | |
1463 | AST = buildASTFromCode(Code: "void f() { int x; noexcept(x = 10); }" ); |
1464 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1465 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1466 | |
1467 | AST = buildASTFromCodeWithArgs(Code: "namespace std { class type_info; }" |
1468 | "void f() { int x; typeid(x = 10); }" , |
1469 | Args: {"-frtti" }); |
1470 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1471 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1472 | |
1473 | AST = buildASTFromCode( |
1474 | Code: "void f() { int x; _Generic(x = 10, int: 0, default: 1); }" ); |
1475 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1476 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1477 | } |
1478 | |
1479 | TEST(ExprMutationAnalyzerTest, NotUnevaluatedExpressions) { |
1480 | auto AST = buildASTFromCode(Code: "void f() { int x; sizeof(int[x++]); }" ); |
1481 | auto Results = |
1482 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1483 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x++" )); |
1484 | |
1485 | AST = buildASTFromCodeWithArgs( |
1486 | Code: "namespace std { class type_info; }" |
1487 | "struct A { virtual ~A(); }; struct B : A {};" |
1488 | "struct X { A& f(); }; void f() { X x; typeid(x.f()); }" , |
1489 | Args: {"-frtti" }); |
1490 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1491 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.f()" )); |
1492 | } |
1493 | |
1494 | // section: special case: smartpointers |
1495 | |
1496 | TEST(ExprMutationAnalyzerTest, UniquePtr) { |
1497 | const std::string UniquePtrDef = |
1498 | "template <class T> struct UniquePtr {" |
1499 | " UniquePtr();" |
1500 | " UniquePtr(const UniquePtr&) = delete;" |
1501 | " UniquePtr(UniquePtr&&);" |
1502 | " UniquePtr& operator=(const UniquePtr&) = delete;" |
1503 | " UniquePtr& operator=(UniquePtr&&);" |
1504 | " T& operator*() const;" |
1505 | " T* operator->() const;" |
1506 | "};" ; |
1507 | |
1508 | auto AST = buildASTFromCode(Code: UniquePtrDef + |
1509 | "void f() { UniquePtr<int> x; *x = 10; }" ); |
1510 | auto Results = |
1511 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1512 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("* x = 10" )); |
1513 | |
1514 | AST = buildASTFromCode(Code: UniquePtrDef + "void f() { UniquePtr<int> x; *x; }" ); |
1515 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1516 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1517 | |
1518 | AST = buildASTFromCode(Code: UniquePtrDef + |
1519 | "void f() { UniquePtr<const int> x; *x; }" ); |
1520 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1521 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1522 | |
1523 | AST = buildASTFromCode(Code: UniquePtrDef + "struct S { int v; };" |
1524 | "void f() { UniquePtr<S> x; x->v; }" ); |
1525 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1526 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x" )); |
1527 | |
1528 | AST = buildASTFromCode(Code: UniquePtrDef + |
1529 | "struct S { int v; };" |
1530 | "void f() { UniquePtr<const S> x; x->v; }" ); |
1531 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1532 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1533 | |
1534 | AST = |
1535 | buildASTFromCode(Code: UniquePtrDef + "struct S { void mf(); };" |
1536 | "void f() { UniquePtr<S> x; x->mf(); }" ); |
1537 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1538 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x" )); |
1539 | |
1540 | AST = buildASTFromCode(Code: UniquePtrDef + |
1541 | "struct S { void mf() const; };" |
1542 | "void f() { UniquePtr<const S> x; x->mf(); }" ); |
1543 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1544 | EXPECT_FALSE(isMutated(Results, AST.get())); |
1545 | |
1546 | AST = buildASTFromCodeWithArgs( |
1547 | Code: UniquePtrDef + "template <class T> void f() { UniquePtr<T> x; x->mf(); }" , |
1548 | Args: {"-fno-delayed-template-parsing" }); |
1549 | Results = match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1550 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x->mf()" )); |
1551 | } |
1552 | |
1553 | // section: complex problems detected on real code |
1554 | |
1555 | TEST(ExprMutationAnalyzerTest, UnevaluatedContext) { |
1556 | const std::string Example = |
1557 | "template <typename T>" |
1558 | "struct to_construct : T { to_construct(int &j) {} };" |
1559 | "template <typename T>" |
1560 | "void placement_new_in_unique_ptr() { int x = 0;" |
1561 | " new to_construct<T>(x);" |
1562 | "}" ; |
1563 | auto AST = |
1564 | buildASTFromCodeWithArgs(Code: Example, Args: {"-fno-delayed-template-parsing" }); |
1565 | auto Results = |
1566 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST->getASTContext()); |
1567 | EXPECT_TRUE(isMutated(Results, AST.get())); |
1568 | EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(x)" )); |
1569 | } |
1570 | |
1571 | TEST(ExprMutationAnalyzerTest, ReproduceFailureMinimal) { |
1572 | const std::string Reproducer = |
1573 | "namespace std {" |
1574 | "template <class T> T &forward(T &A) { return static_cast<T&&>(A); }" |
1575 | "template <class T> struct __bind {" |
1576 | " T f;" |
1577 | " template <class V> __bind(T v, V &&) : f(forward(v)) {}" |
1578 | "};" |
1579 | "}" |
1580 | "void f() {" |
1581 | " int x = 42;" |
1582 | " auto Lambda = [] {};" |
1583 | " std::__bind<decltype(Lambda)>(Lambda, x);" |
1584 | "}" ; |
1585 | auto AST11 = buildASTFromCodeWithArgs(Code: Reproducer, Args: {"-std=c++11" }); |
1586 | auto Results11 = |
1587 | match(Matcher: withEnclosingCompound(Matcher: declRefTo(Name: "x" )), Context&: AST11->getASTContext()); |
1588 | EXPECT_FALSE(isMutated(Results11, AST11.get())); |
1589 | } |
1590 | } // namespace clang |
1591 | |