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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp