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