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

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