1 //===---------- ExprMutationAnalyzerTest.cpp ------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
21 using namespace clang::ast_matchers
;
22 using ::testing::ElementsAre
;
23 using ::testing::ResultOf
;
24 using ::testing::Values
;
28 using ExprMatcher
= internal::Matcher
<Expr
>;
29 using StmtMatcher
= internal::Matcher
<Stmt
>;
31 std::unique_ptr
<ASTUnit
>
32 buildASTFromCodeWithArgs(const Twine
&Code
,
33 const std::vector
<std::string
> &Args
) {
34 SmallString
<1024> CodeStorage
;
36 tooling::buildASTFromCodeWithArgs(Code
.toStringRef(CodeStorage
), Args
);
37 EXPECT_FALSE(AST
->getDiagnostics().hasErrorOccurred());
41 std::unique_ptr
<ASTUnit
> buildASTFromCode(const Twine
&Code
) {
42 return buildASTFromCodeWithArgs(Code
, {});
45 ExprMatcher
declRefTo(StringRef Name
) {
46 return declRefExpr(to(namedDecl(hasName(Name
))));
49 StmtMatcher
withEnclosingCompound(ExprMatcher Matcher
) {
50 return expr(Matcher
, hasAncestor(compoundStmt().bind("stmt"))).bind("expr");
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
);
60 SmallVector
<std::string
, 1>
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());
66 for (const auto *E
= selectFirst
<Expr
>("expr", Results
); E
!= nullptr;) {
67 const Stmt
*By
= Analyzer
.findMutation(E
);
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
);
80 std::string
removeSpace(std::string s
) {
81 s
.erase(std::remove_if(s
.begin(), s
.end(),
82 [](char c
) { return llvm::isSpace(c
); }),
87 const std::string StdRemoveReference
=
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; }; }";
93 const std::string StdMove
=
95 "template<class T> typename remove_reference<T>::type&& "
96 "move(T&& t) noexcept {"
97 "return static_cast<typename remove_reference<T>::type&&>(t); } }";
99 const std::string StdForward
=
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; } }";
108 TEST(ExprMutationAnalyzerTest
, Trivial
) {
109 const auto AST
= buildASTFromCode("void f() { int x; x; }");
111 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
112 EXPECT_FALSE(isMutated(Results
, AST
.get()));
115 class AssignmentTest
: public ::testing::TestWithParam
<std::string
> {};
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.
122 TEST_P(AssignmentTest
, AssignmentModifies
) {
123 // Test the detection of the raw expression modifications.
125 const std::string ModExpr
= "x " + GetParam() + " 10";
126 const auto AST
= buildASTFromCode("void f() { int x; " + ModExpr
+ "; }");
128 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
129 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
132 // Test the detection if the expression is surrounded by parens.
134 const std::string ModExpr
= "(x) " + GetParam() + " 10";
135 const auto AST
= buildASTFromCode("void f() { int x; " + ModExpr
+ "; }");
137 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
138 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
141 // Test the detection if the comma operator yields the expression as result.
143 const std::string ModExpr
= "x " + GetParam() + " 10";
144 const auto AST
= buildASTFromCodeWithArgs(
145 "void f() { int x, y; y, " + ModExpr
+ "; }", {"-Wno-unused-value"});
147 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
148 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
151 // Ensure no detection if the comma operator does not yield the expression as
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"});
158 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
159 EXPECT_FALSE(isMutated(Results
, AST
.get()));
162 // Test the detection if the a ternary operator can result in the expression.
164 const std::string ModExpr
= "(y != 0 ? y : x) " + GetParam() + " 10";
166 buildASTFromCode("void f() { int y = 0, x; " + ModExpr
+ "; }");
168 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
169 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
172 // Test the detection if the a ternary operator can result in the expression
173 // through multiple nesting of ternary operators.
175 const std::string ModExpr
=
176 "(y != 0 ? (y > 5 ? y : x) : (y)) " + GetParam() + " 10";
178 buildASTFromCode("void f() { int y = 0, x; " + ModExpr
+ "; }");
180 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
181 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
184 // Test the detection if the a ternary operator can result in the expression
185 // with additional parens.
187 const std::string ModExpr
= "(y != 0 ? (y) : ((x))) " + GetParam() + " 10";
189 buildASTFromCode("void f() { int y = 0, x; " + ModExpr
+ "; }");
191 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
192 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
195 // Test the detection for the binary conditional operator.
197 const std::string ModExpr
= "(y ?: x) " + GetParam() + " 10";
199 buildASTFromCode("void f() { int y = 0, x; " + ModExpr
+ "; }");
201 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
202 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
206 INSTANTIATE_TEST_CASE_P(AllAssignmentOperators
, AssignmentTest
,
207 Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
208 "^=", "<<=", ">>="), );
210 TEST(ExprMutationAnalyzerTest
, AssignmentConditionalWithInheritance
) {
211 const auto AST
= buildASTFromCode("struct Base {void nonconst(); };"
212 "struct Derived : Base {};"
215 " Base &b = true ? x : y;"
219 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
220 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("b", "b.nonconst()"));
223 class IncDecTest
: public ::testing::TestWithParam
<std::string
> {};
225 TEST_P(IncDecTest
, IncDecModifies
) {
226 const std::string ModExpr
= GetParam();
227 const auto AST
= buildASTFromCode("void f() { int x; " + ModExpr
+ "; }");
229 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
230 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
233 INSTANTIATE_TEST_CASE_P(AllIncDecOperators
, IncDecTest
,
234 Values("++x", "--x", "x++", "x--", "++(x)", "--(x)",
235 "(x)++", "(x)--"), );
237 // Section: member functions
239 TEST(ExprMutationAnalyzerTest
, NonConstMemberFunc
) {
240 const auto AST
= buildASTFromCode(
241 "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
243 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
244 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.mf()"));
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"});
253 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
254 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.mf<T>()"));
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()"));
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()"));
269 TEST(ExprMutationAnalyzerTest
, ConstMemberFunc
) {
270 const auto AST
= buildASTFromCode(
271 "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
273 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
274 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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"});
283 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
284 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.push_back(T())"));
287 // Section: overloaded operators
289 TEST(ExprMutationAnalyzerTest
, NonConstOperator
) {
290 const auto AST
= buildASTFromCode(
291 "void f() { struct Foo { Foo& operator=(int); }; Foo x; x = 10; }");
293 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
294 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x = 10"));
297 TEST(ExprMutationAnalyzerTest
, ConstOperator
) {
298 const auto AST
= buildASTFromCode(
299 "void f() { struct Foo { int operator()() const; }; Foo x; x(); }");
301 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
302 EXPECT_FALSE(isMutated(Results
, AST
.get()));
305 TEST(ExprMutationAnalyzerTest
, UnresolvedOperator
) {
306 const auto AST
= buildASTFromCodeWithArgs(
307 "template <typename Stream> void input_operator_template() {"
308 "Stream x; unsigned y = 42;"
310 {"-fno-delayed-template-parsing"});
312 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
313 EXPECT_TRUE(isMutated(Results
, AST
.get()));
316 // Section: expression as call argument
318 TEST(ExprMutationAnalyzerTest
, ByValueArgument
) {
319 auto AST
= buildASTFromCode("void g(int); void f() { int x; g(x); }");
321 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
322 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
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()));
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()));
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()));
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()));
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()));
354 TEST(ExprMutationAnalyzerTest
, ByConstValueArgument
) {
355 auto AST
= buildASTFromCode("void g(const int); void f() { int x; g(x); }");
357 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
358 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
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()));
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()));
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()));
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()));
386 TEST(ExprMutationAnalyzerTest
, ByNonConstRefArgument
) {
387 auto AST
= buildASTFromCode("void g(int&); void f() { int x; g(x); }");
389 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
390 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
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)"));
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)"));
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)"));
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)"));
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)"));
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"));
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"));
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"));
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)"));
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); "
449 {"-fno-delayed-template-parsing"});
451 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
452 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("parser(x)"));
455 TEST(ExprMutationAnalyzerTest
, ByConstRefArgument
) {
456 auto AST
= buildASTFromCode("void g(const int&); void f() { int x; g(x); }");
458 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
459 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
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()));
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()));
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()));
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()));
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()));
495 TEST(ExprMutationAnalyzerTest
, ByNonConstRRefArgument
) {
496 auto AST
= buildASTFromCode(
497 "void g(int&&); void f() { int x; g(static_cast<int &&>(x)); }");
499 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
500 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
501 ElementsAre("g(static_cast<int &&>(x))"));
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"));
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)"));
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)"));
522 TEST(ExprMutationAnalyzerTest
, ByConstRRefArgument
) {
523 auto AST
= buildASTFromCode(
524 "void g(const int&&); void f() { int x; g(static_cast<int&&>(x)); }");
526 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
527 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
528 ElementsAre("static_cast<int &&>(x)"));
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)"));
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)"));
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)"));
549 // section: explicit std::move and std::forward testing
551 TEST(ExprMutationAnalyzerTest
, Move
) {
552 auto AST
= buildASTFromCode(StdRemoveReference
+ StdMove
+
553 "void f() { struct A {}; A x; std::move(x); }");
555 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
556 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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"));
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()));
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()));
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)"));
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)"));
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()));
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()));
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)"));
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()));
613 TEST(ExprMutationAnalyzerTest
, Forward
) {
615 buildASTFromCode(StdRemoveReference
+ StdForward
+
616 "void f() { struct A {}; A x; std::forward<A &>(x); }");
618 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
619 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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"));
629 // section: template constellations that prohibit reasoning about modifications
630 // as it depends on instantiations.
632 TEST(ExprMutationAnalyzerTest
, CallUnresolved
) {
634 buildASTFromCodeWithArgs("template <class T> void f() { T x; g(x); }",
635 {"-fno-delayed-template-parsing"});
637 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
638 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
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)"));
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)"));
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)"));
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)"));
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)"));
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)"));
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)"));
685 // section: return values
687 TEST(ExprMutationAnalyzerTest
, ReturnAsValue
) {
688 auto AST
= buildASTFromCode("int f() { int x; return x; }");
690 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
691 EXPECT_FALSE(isMutated(Results
, AST
.get()));
693 AST
= buildASTFromCode("int* f() { int* x; return x; }");
694 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
695 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
703 TEST(ExprMutationAnalyzerTest
, ReturnAsNonConstRef
) {
704 const auto AST
= buildASTFromCode("int& f() { int x; return x; }");
706 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
707 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("return x;"));
710 TEST(ExprMutationAnalyzerTest
, ReturnAsConstRef
) {
711 const auto AST
= buildASTFromCode("const int& f() { int x; return x; }");
713 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
714 EXPECT_FALSE(isMutated(Results
, AST
.get()));
717 TEST(ExprMutationAnalyzerTest
, ReturnAsNonConstRRef
) {
719 buildASTFromCode("int&& f() { int x; return static_cast<int &&>(x); }");
721 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
722 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
723 ElementsAre("static_cast<int &&>(x)"));
726 TEST(ExprMutationAnalyzerTest
, ReturnAsConstRRef
) {
727 const auto AST
= buildASTFromCode(
728 "const int&& f() { int x; return static_cast<int&&>(x); }");
730 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
731 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
732 ElementsAre("static_cast<int &&>(x)"));
735 // section: taking the address of a variable and pointers
737 TEST(ExprMutationAnalyzerTest
, TakeAddress
) {
738 const auto AST
= buildASTFromCode("void g(int*); void f() { int x; g(&x); }");
740 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
741 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("&x"));
744 TEST(ExprMutationAnalyzerTest
, ArrayToPointerDecay
) {
746 buildASTFromCode("void g(int*); void f() { int x[2]; g(x); }");
748 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
749 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x"));
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; };"
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"));
768 // section: special case: all created references are non-mutating themself
769 // and therefore all become 'const'/the value is not modified!
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; }");
776 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
777 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
778 ElementsAre("r0", "r1", "r2", "r3", "r3 = 10"));
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"));
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;}");
794 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
795 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
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()));
809 TEST(ExprMutationAnalyzerTest
, FollowConditionalRefModified
) {
810 const auto AST
= buildASTFromCode(
811 "void f() { int x, y; bool b; int &r = b ? x : y; r = 10; }");
813 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
814 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("r", "r = 10"));
817 TEST(ExprMutationAnalyzerTest
, FollowConditionalRefNotModified
) {
819 buildASTFromCode("void f() { int x, y; bool b; int& r = b ? x : y; }");
821 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
822 EXPECT_FALSE(isMutated(Results
, AST
.get()));
825 TEST(ExprMutationAnalyzerTest
, FollowFuncArgModified
) {
826 auto AST
= buildASTFromCode("template <class T> void g(T&& t) { t = 10; }"
827 "void f() { int x; g(x); }");
829 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
830 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
832 AST
= buildASTFromCode(
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)"));
839 AST
= buildASTFromCode(
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()));
848 AST
= buildASTFromCode(
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()));
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"));
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"));
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"));
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)"));
885 TEST(ExprMutationAnalyzerTest
, FollowFuncArgNotModified
) {
886 auto AST
= buildASTFromCode("template <class T> void g(T&&) {}"
887 "void f() { int x; g(x); }");
889 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
890 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
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()));
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()));
907 AST
= buildASTFromCode(
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()));
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()));
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()));
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()));
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()));
942 // section: builtin arrays
944 TEST(ExprMutationAnalyzerTest
, ArrayElementModified
) {
945 const auto AST
= buildASTFromCode("void f() { int x[2]; x[0] = 10; }");
947 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
948 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x[0] = 10"));
951 TEST(ExprMutationAnalyzerTest
, ArrayElementNotModified
) {
952 const auto AST
= buildASTFromCode("void f() { int x[2]; x[0]; }");
954 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
955 EXPECT_FALSE(isMutated(Results
, AST
.get()));
958 // section: member modifications
960 TEST(ExprMutationAnalyzerTest
, NestedMemberModified
) {
962 buildASTFromCode("void f() { struct A { int vi; }; struct B { A va; }; "
963 "struct C { B vb; }; C x; x.vb.va.vi = 10; }");
965 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
966 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.vb.va.vi = 10"));
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"));
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"));
982 TEST(ExprMutationAnalyzerTest
, NestedMemberNotModified
) {
984 buildASTFromCode("void f() { struct A { int vi; }; struct B { A va; }; "
985 "struct C { B vb; }; C x; x.vb.va.vi; }");
987 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
988 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
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()));
1005 TEST(ExprMutationAnalyzerTest
, CastToValue
) {
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()));
1013 TEST(ExprMutationAnalyzerTest
, CastToRefModified
) {
1015 buildASTFromCode("void f() { int x; static_cast<int &>(x) = 10; }");
1017 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1018 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
1019 ElementsAre("static_cast<int &>(x)"));
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)"));
1028 TEST(ExprMutationAnalyzerTest
, CastToRefNotModified
) {
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)"));
1037 TEST(ExprMutationAnalyzerTest
, CastToConstRef
) {
1039 buildASTFromCode("void f() { int x; static_cast<const int&>(x); }");
1041 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1042 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
1050 // section: comma expressions
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()));
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()));
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()));
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()));
1088 TEST(ExprMutationAnalyzerTest
, CommaExprWithCallExpr
) {
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()));
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"});
1104 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1105 EXPECT_TRUE(isMutated(Results
, AST
.get()));
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()));
1114 TEST(ExprMutationAnalyzerTest
, CommaExprParmRef
) {
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()));
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()));
1135 TEST(ExprMutationAnalyzerTest
, CommaExprAsReturnAsValue
) {
1136 auto AST
= buildASTFromCodeWithArgs("int f() { int x, y; return (x, y); }",
1137 {"-Wno-unused-value"});
1139 match(withEnclosingCompound(declRefTo("y")), AST
->getASTContext());
1140 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
1151 TEST(ExprMutationAnalyzerTest
, CommaExprAsArrayToPointerDecay
) {
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()));
1161 TEST(ExprMutationAnalyzerTest
, CommaExprAsUniquePtr
) {
1162 const std::string UniquePtrDef
= "template <class T> struct UniquePtr {"
1164 " UniquePtr(const UniquePtr&) = delete;"
1165 " T& operator*() const;"
1166 " T* operator->() const;"
1168 const auto AST
= buildASTFromCodeWithArgs(
1169 UniquePtrDef
+ "template <class T> void f() "
1170 "{ UniquePtr<T> x; UniquePtr<T> y;"
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()));
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"));
1188 // section: lambda captures
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()));
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()));
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;}")));
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;}")));
1220 // section: range-for loops
1222 TEST(ExprMutationAnalyzerTest
, RangeForArrayByRefModified
) {
1224 buildASTFromCode("void f() { int x[2]; for (int& e : x) e = 10; }");
1226 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1227 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
1228 ElementsAre("for (int &e : x)\n e = 10;"));
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;"));
1237 TEST(ExprMutationAnalyzerTest
, RangeForArrayByRefModifiedByImplicitInit
) {
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()));
1245 TEST(ExprMutationAnalyzerTest
, RangeForArrayByValue
) {
1246 auto AST
= buildASTFromCode("void f() { int x[2]; for (int e : x) e = 10; }");
1248 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1249 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
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()));
1263 TEST(ExprMutationAnalyzerTest
, RangeForArrayByConstRef
) {
1265 buildASTFromCode("void f() { int x[2]; for (const int& e : x) e; }");
1267 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1268 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
1276 TEST(ExprMutationAnalyzerTest
, RangeForNonArrayByRefModified
) {
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;"));
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()));
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()));
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()));
1312 // section: unevaluated expressions
1314 TEST(ExprMutationAnalyzerTest
, UnevaluatedExpressions
) {
1315 auto AST
= buildASTFromCode("void f() { int x, y; decltype(x = 10) z = y; }");
1317 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1318 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
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()));
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()));
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()));
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()));
1340 AST
= buildASTFromCodeWithArgs("namespace std { class type_info; }"
1341 "void f() { int x; typeid(x = 10); }",
1343 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1344 EXPECT_FALSE(isMutated(Results
, AST
.get()));
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()));
1352 TEST(ExprMutationAnalyzerTest
, NotUnevaluatedExpressions
) {
1353 auto AST
= buildASTFromCode("void f() { int x; sizeof(int[x++]); }");
1355 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1356 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x++"));
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()); }",
1363 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1364 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.f()"));
1367 // section: special case: smartpointers
1369 TEST(ExprMutationAnalyzerTest
, UniquePtr
) {
1370 const std::string UniquePtrDef
=
1371 "template <class T> struct 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;"
1381 auto AST
= buildASTFromCode(UniquePtrDef
+
1382 "void f() { UniquePtr<int> x; *x = 10; }");
1384 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1385 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("* x = 10"));
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()));
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()));
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"));
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()));
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"));
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()));
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()"));
1426 // section: complex problems detected on real code
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);"
1437 buildASTFromCodeWithArgs(Example
, {"-fno-delayed-template-parsing"});
1439 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1440 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1441 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("(x)"));
1444 TEST(ExprMutationAnalyzerTest
, ReproduceFailureMinimal
) {
1445 const std::string Reproducer
=
1447 "template <class T> T forward(T & A) { return static_cast<T&&>(A); }"
1448 "template <class T> struct __bind {"
1450 " template <class V> __bind(T v, V &&) : f(forward(v)) {}"
1455 " auto Lambda = [] {};"
1456 " std::__bind<decltype(Lambda)>(Lambda, x);"
1458 auto AST11
= buildASTFromCodeWithArgs(Reproducer
, {"-std=c++11"});
1460 match(withEnclosingCompound(declRefTo("x")), AST11
->getASTContext());
1461 EXPECT_FALSE(isMutated(Results11
, AST11
.get()));
1463 } // namespace clang