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