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