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/Frontend/ASTUnit.h"
14 #include "clang/Tooling/Tooling.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
)).bind("decl")));
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 bool isDeclMutated(const SmallVectorImpl
<BoundNodes
> &Results
, ASTUnit
*AST
) {
61 const auto *const S
= selectFirst
<Stmt
>("stmt", Results
);
62 const auto *const D
= selectFirst
<Decl
>("decl", Results
);
63 TraversalKindScope
RAII(AST
->getASTContext(), TK_AsIs
);
64 return ExprMutationAnalyzer(*S
, AST
->getASTContext()).isMutated(D
);
67 SmallVector
<std::string
, 1>
68 mutatedBy(const SmallVectorImpl
<BoundNodes
> &Results
, ASTUnit
*AST
) {
69 const auto *const S
= selectFirst
<Stmt
>("stmt", Results
);
70 SmallVector
<std::string
, 1> Chain
;
71 ExprMutationAnalyzer
Analyzer(*S
, AST
->getASTContext());
73 for (const auto *E
= selectFirst
<Expr
>("expr", Results
); E
!= nullptr;) {
74 const Stmt
*By
= Analyzer
.findMutation(E
);
79 llvm::raw_string_ostream
Stream(Buffer
);
80 By
->printPretty(Stream
, nullptr, AST
->getASTContext().getPrintingPolicy());
81 Chain
.emplace_back(StringRef(Buffer
).trim().str());
82 E
= dyn_cast
<DeclRefExpr
>(By
);
87 std::string
removeSpace(std::string s
) {
88 s
.erase(std::remove_if(s
.begin(), s
.end(),
89 [](char c
) { return llvm::isSpace(c
); }),
94 const std::string StdRemoveReference
=
96 "template<class T> struct remove_reference { typedef T type; };"
97 "template<class T> struct remove_reference<T&> { typedef T type; };"
98 "template<class T> struct remove_reference<T&&> { typedef T type; }; }";
100 const std::string StdMove
=
102 "template<class T> typename remove_reference<T>::type&& "
103 "move(T&& t) noexcept {"
104 "return static_cast<typename remove_reference<T>::type&&>(t); } }";
106 const std::string StdForward
=
108 "template<class T> T&& "
109 "forward(typename remove_reference<T>::type& t) noexcept { return t; }"
110 "template<class T> T&& "
111 "forward(typename remove_reference<T>::type&& t) noexcept { return t; } }";
115 TEST(ExprMutationAnalyzerTest
, Trivial
) {
116 const auto AST
= buildASTFromCode("void f() { int x; x; }");
118 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
119 EXPECT_FALSE(isMutated(Results
, AST
.get()));
122 class AssignmentTest
: public ::testing::TestWithParam
<std::string
> {};
124 // This test is for the most basic and direct modification of a variable,
125 // assignment to it (e.g. `x = 10;`).
126 // It additionally tests that references to a variable are not only captured
127 // directly but expressions that result in the variable are handled, too.
128 // This includes the comma operator, parens and the ternary operator.
129 TEST_P(AssignmentTest
, AssignmentModifies
) {
130 // Test the detection of the raw expression modifications.
132 const std::string ModExpr
= "x " + GetParam() + " 10";
133 const auto AST
= buildASTFromCode("void f() { int x; " + ModExpr
+ "; }");
135 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
136 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
139 // Test the detection if the expression is surrounded by parens.
141 const std::string ModExpr
= "(x) " + GetParam() + " 10";
142 const auto AST
= buildASTFromCode("void f() { int x; " + ModExpr
+ "; }");
144 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
145 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
148 // Test the detection if the comma operator yields the expression as result.
150 const std::string ModExpr
= "x " + GetParam() + " 10";
151 const auto AST
= buildASTFromCodeWithArgs(
152 "void f() { int x, y; y, " + ModExpr
+ "; }", {"-Wno-unused-value"});
154 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
155 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
158 // Ensure no detection if the comma operator does not yield the expression as
161 const std::string ModExpr
= "y, x, y " + GetParam() + " 10";
162 const auto AST
= buildASTFromCodeWithArgs(
163 "void f() { int x, y; " + ModExpr
+ "; }", {"-Wno-unused-value"});
165 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
166 EXPECT_FALSE(isMutated(Results
, AST
.get()));
169 // Test the detection if the a ternary operator can result in the expression.
171 const std::string ModExpr
= "(y != 0 ? y : x) " + GetParam() + " 10";
173 buildASTFromCode("void f() { int y = 0, x; " + ModExpr
+ "; }");
175 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
176 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
179 // Test the detection if the a ternary operator can result in the expression
180 // through multiple nesting of ternary operators.
182 const std::string ModExpr
=
183 "(y != 0 ? (y > 5 ? y : x) : (y)) " + GetParam() + " 10";
185 buildASTFromCode("void f() { int y = 0, x; " + ModExpr
+ "; }");
187 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
188 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
191 // Test the detection if the a ternary operator can result in the expression
192 // with additional parens.
194 const std::string ModExpr
= "(y != 0 ? (y) : ((x))) " + GetParam() + " 10";
196 buildASTFromCode("void f() { int y = 0, x; " + ModExpr
+ "; }");
198 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
199 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
202 // Test the detection for the binary conditional operator.
204 const std::string ModExpr
= "(y ?: x) " + GetParam() + " 10";
206 buildASTFromCode("void f() { int y = 0, x; " + ModExpr
+ "; }");
208 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
209 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
213 INSTANTIATE_TEST_SUITE_P(AllAssignmentOperators
, AssignmentTest
,
214 Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=",
215 "^=", "<<=", ">>=") );
217 TEST(ExprMutationAnalyzerTest
, AssignmentConditionalWithInheritance
) {
218 const auto AST
= buildASTFromCode("struct Base {void nonconst(); };"
219 "struct Derived : Base {};"
222 " Base &b = true ? x : y;"
226 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
227 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("b", "b.nonconst()"));
230 class IncDecTest
: public ::testing::TestWithParam
<std::string
> {};
232 TEST_P(IncDecTest
, IncDecModifies
) {
233 const std::string ModExpr
= GetParam();
234 const auto AST
= buildASTFromCode("void f() { int x; " + ModExpr
+ "; }");
236 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
237 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre(ModExpr
));
240 INSTANTIATE_TEST_SUITE_P(AllIncDecOperators
, IncDecTest
,
241 Values("++x", "--x", "x++", "x--", "++(x)", "--(x)",
244 // Section: member functions
246 TEST(ExprMutationAnalyzerTest
, NonConstMemberFunc
) {
247 const auto AST
= buildASTFromCode(
248 "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }");
250 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
251 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.mf()"));
254 TEST(ExprMutationAnalyzerTest
, AssumedNonConstMemberFunc
) {
255 auto AST
= buildASTFromCodeWithArgs(
256 "struct X { template <class T> void mf(); };"
257 "template <class T> void f() { X x; x.mf<T>(); }",
258 {"-fno-delayed-template-parsing"});
260 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
261 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.mf<T>()"));
263 AST
= buildASTFromCodeWithArgs("template <class T> void f() { 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()"));
268 AST
= buildASTFromCodeWithArgs(
269 "template <class T> struct X;"
270 "template <class T> void f() { X<T> x; x.mf(); }",
271 {"-fno-delayed-template-parsing"});
272 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
273 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.mf()"));
276 TEST(ExprMutationAnalyzerTest
, ConstMemberFunc
) {
277 const auto AST
= buildASTFromCode(
278 "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }");
280 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
281 EXPECT_FALSE(isMutated(Results
, AST
.get()));
284 TEST(ExprMutationAnalyzerTest
, TypeDependentMemberCall
) {
285 const auto AST
= buildASTFromCodeWithArgs(
286 "template <class T> class vector { void push_back(T); }; "
287 "template <class T> void f() { vector<T> x; x.push_back(T()); }",
288 {"-fno-delayed-template-parsing"});
290 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
291 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.push_back(T())"));
294 TEST(ExprMutationAnalyzerTest
, MemberPointerMemberCall
) {
297 buildASTFromCode("struct X {};"
298 "using T = int (X::*)();"
299 "void f(X &x, T m) { X &ref = x; (ref.*m)(); }");
301 match(withEnclosingCompound(declRefTo("ref")), AST
->getASTContext());
302 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("(ref .* m)()"));
306 buildASTFromCode("struct X {};"
307 "using T = int (X::*)();"
308 "void f(X &x, T const m) { X &ref = x; (ref.*m)(); }");
310 match(withEnclosingCompound(declRefTo("ref")), AST
->getASTContext());
311 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("(ref .* m)()"));
315 buildASTFromCode("struct X {};"
316 "using T = int (X::*)() const;"
317 "void f(X &x, T m) { X &ref = x; (ref.*m)(); }");
319 match(withEnclosingCompound(declRefTo("ref")), AST
->getASTContext());
320 EXPECT_FALSE(isMutated(Results
, AST
.get()));
324 // Section: overloaded operators
326 TEST(ExprMutationAnalyzerTest
, NonConstOperator
) {
327 const auto AST
= buildASTFromCode(
328 "void f() { struct Foo { Foo& operator=(int); }; Foo x; x = 10; }");
330 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
331 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x = 10"));
334 TEST(ExprMutationAnalyzerTest
, ConstOperator
) {
335 const auto AST
= buildASTFromCode(
336 "void f() { struct Foo { int operator()() const; }; Foo x; x(); }");
338 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
339 EXPECT_FALSE(isMutated(Results
, AST
.get()));
342 TEST(ExprMutationAnalyzerTest
, UnresolvedOperator
) {
343 const auto AST
= buildASTFromCodeWithArgs(
344 "template <typename Stream> void input_operator_template() {"
345 "Stream x; unsigned y = 42;"
347 {"-fno-delayed-template-parsing"});
349 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
350 EXPECT_TRUE(isMutated(Results
, AST
.get()));
353 TEST(ExprMutationAnalyzerTest
, DependentOperatorWithNonDependentOperand
) {
355 // The expression to check may not be the dependent operand in a dependent
358 // Explicitly not declaring a (templated) stream operator
359 // so the `<<` is a `binaryOperator` with a dependent type.
360 const auto AST
= buildASTFromCodeWithArgs(
362 "template <typename T> void f() { T t; Stream x; x << t; }",
363 {"-fno-delayed-template-parsing"});
365 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
366 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x << t"));
369 TEST(ExprMutationAnalyzerTest
, FoldExpression
) {
371 // A fold expression may contain `Exp` as it's initializer.
372 // We don't know if the operator modifies `Exp` because the
373 // operator is type dependent due to the parameter pack.
374 auto AST
= buildASTFromCodeWithArgs(
376 "template <typename... Args> void concatenate(Args... args) "
377 "{ Stream x; (x << ... << args); }",
378 {"-fno-delayed-template-parsing"});
380 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
381 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("(x << ... << args)"));
383 AST
= buildASTFromCodeWithArgs(
385 "template <typename... Args> void concatenate(Args... args) "
386 "{ Stream x; (args << ... << x); }",
387 {"-fno-delayed-template-parsing"});
388 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
389 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("(args << ... << x)"));
391 AST
= buildASTFromCodeWithArgs(
393 "template <typename... Args> void concatenate(Args... args) "
394 "{ Stream x; (..., (x << args)); }",
395 {"-fno-delayed-template-parsing"});
396 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
397 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x << args"));
400 // Section: expression as call argument
402 TEST(ExprMutationAnalyzerTest
, ByValueArgument
) {
403 auto AST
= buildASTFromCode("void g(int); void f() { int x; g(x); }");
405 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
406 EXPECT_FALSE(isMutated(Results
, AST
.get()));
408 AST
= buildASTFromCode("void g(int*); void f() { int* x; g(x); }");
409 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
410 EXPECT_FALSE(isMutated(Results
, AST
.get()));
412 AST
= buildASTFromCode("typedef int* IntPtr;"
413 "void g(IntPtr); void f() { int* x; g(x); }");
414 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
415 EXPECT_FALSE(isMutated(Results
, AST
.get()));
417 AST
= buildASTFromCode(
418 "struct A {}; A operator+(A, int); void f() { A x; x + 1; }");
419 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
420 EXPECT_FALSE(isMutated(Results
, AST
.get()));
422 AST
= buildASTFromCode("void f() { struct A { A(int); }; int x; A y(x); }");
423 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
424 EXPECT_FALSE(isMutated(Results
, AST
.get()));
426 AST
= buildASTFromCode("struct A { A(); A& operator=(A); };"
427 "void f() { A x, y; y = x; }");
428 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
429 EXPECT_FALSE(isMutated(Results
, AST
.get()));
431 AST
= buildASTFromCode(
432 "template <int> struct A { A(); A(const A&); static void mf(A) {} };"
433 "void f() { A<0> x; A<0>::mf(x); }");
434 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
435 EXPECT_FALSE(isMutated(Results
, AST
.get()));
438 TEST(ExprMutationAnalyzerTest
, ByConstValueArgument
) {
439 auto AST
= buildASTFromCode("void g(const int); void f() { int x; g(x); }");
441 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
442 EXPECT_FALSE(isMutated(Results
, AST
.get()));
444 AST
= buildASTFromCode("void g(int* const); void f() { int* x; g(x); }");
445 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
446 EXPECT_FALSE(isMutated(Results
, AST
.get()));
448 AST
= buildASTFromCode("typedef int* const CIntPtr;"
449 "void g(CIntPtr); void f() { int* x; g(x); }");
450 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
451 EXPECT_FALSE(isMutated(Results
, AST
.get()));
453 AST
= buildASTFromCode(
454 "struct A {}; A operator+(const A, int); void f() { A x; x + 1; }");
455 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
456 EXPECT_FALSE(isMutated(Results
, AST
.get()));
458 AST
= buildASTFromCode(
459 "void f() { struct A { A(const int); }; int x; A y(x); }");
460 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
461 EXPECT_FALSE(isMutated(Results
, AST
.get()));
463 AST
= buildASTFromCode("template <int> struct A { A(); A(const A&);"
464 "static void mf(const A&) {} };"
465 "void f() { A<0> x; A<0>::mf(x); }");
466 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
467 EXPECT_FALSE(isMutated(Results
, AST
.get()));
470 TEST(ExprMutationAnalyzerTest
, ByNonConstRefArgument
) {
471 auto AST
= buildASTFromCode("void g(int&); void f() { int x; g(x); }");
473 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
474 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
476 AST
= buildASTFromCode("typedef int& IntRef;"
477 "void g(IntRef); void f() { int x; g(x); }");
478 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
479 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
481 AST
= buildASTFromCode("template <class T> using TRef = T&;"
482 "void g(TRef<int>); void f() { int x; g(x); }");
483 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
484 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
486 AST
= buildASTFromCode(
487 "template <class T> struct identity { using type = T; };"
488 "template <class T, class U = T&> void g(typename identity<U>::type);"
489 "void f() { int x; g<int>(x); }");
490 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
491 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g<int>(x)"));
493 AST
= buildASTFromCode("typedef int* IntPtr;"
494 "void g(IntPtr&); void f() { int* x; g(x); }");
495 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
496 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
498 AST
= buildASTFromCode("typedef int* IntPtr; typedef IntPtr& IntPtrRef;"
499 "void g(IntPtrRef); void f() { int* x; g(x); }");
500 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
501 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
503 AST
= buildASTFromCode(
504 "struct A {}; A operator+(A&, int); void f() { A x; x + 1; }");
505 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
506 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x + 1"));
508 AST
= buildASTFromCode("void f() { struct A { A(int&); }; int x; A y(x); }");
509 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
510 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x"));
512 AST
= buildASTFromCode("void f() { struct A { A(); A(A&); }; A x; A y(x); }");
513 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
514 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x"));
516 AST
= buildASTFromCode(
517 "template <int> struct A { A(); A(const A&); static void mf(A&) {} };"
518 "void f() { A<0> x; A<0>::mf(x); }");
519 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
520 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("A<0>::mf(x)"));
523 TEST(ExprMutationAnalyzerTest
, ByNonConstRefArgumentFunctionTypeDependent
) {
524 auto AST
= buildASTFromCodeWithArgs(
525 "enum MyEnum { foo, bar };"
526 "void tryParser(unsigned& first, MyEnum Type) { first++, (void)Type; }"
527 "template <MyEnum Type> void parse() {"
528 " auto parser = [](unsigned& first) { first++; tryParser(first, Type); "
533 {"-fno-delayed-template-parsing"});
535 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
536 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("parser(x)"));
539 TEST(ExprMutationAnalyzerTest
, ByConstRefArgument
) {
540 auto AST
= buildASTFromCode("void g(const int&); void f() { int x; g(x); }");
542 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
543 EXPECT_FALSE(isMutated(Results
, AST
.get()));
545 AST
= buildASTFromCode("typedef const int& CIntRef;"
546 "void g(CIntRef); void f() { int x; g(x); }");
547 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
548 EXPECT_FALSE(isMutated(Results
, AST
.get()));
550 AST
= buildASTFromCode("template <class T> using CTRef = const T&;"
551 "void g(CTRef<int>); void f() { int x; g(x); }");
552 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
553 EXPECT_FALSE(isMutated(Results
, AST
.get()));
556 buildASTFromCode("template <class T> struct identity { using type = T; };"
557 "template <class T, class U = const T&>"
558 "void g(typename identity<U>::type);"
559 "void f() { int x; g<int>(x); }");
560 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
561 EXPECT_FALSE(isMutated(Results
, AST
.get()));
563 AST
= buildASTFromCode(
564 "struct A {}; A operator+(const A&, int); void f() { A x; x + 1; }");
565 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
566 EXPECT_FALSE(isMutated(Results
, AST
.get()));
568 AST
= buildASTFromCode(
569 "void f() { struct A { A(const int&); }; int x; A y(x); }");
570 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
571 EXPECT_FALSE(isMutated(Results
, AST
.get()));
573 AST
= buildASTFromCode(
574 "void f() { struct A { A(); A(const A&); }; A x; A y(x); }");
575 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
576 EXPECT_FALSE(isMutated(Results
, AST
.get()));
579 TEST(ExprMutationAnalyzerTest
, ByNonConstRRefArgument
) {
580 auto AST
= buildASTFromCode(
581 "void g(int&&); void f() { int x; g(static_cast<int &&>(x)); }");
583 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
584 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
585 ElementsAre("g(static_cast<int &&>(x))"));
587 AST
= buildASTFromCode("struct A {}; A operator+(A&&, int);"
588 "void f() { A x; static_cast<A &&>(x) + 1; }");
589 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
590 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
591 ElementsAre("static_cast<A &&>(x) + 1"));
593 AST
= buildASTFromCode("void f() { struct A { A(int&&); }; "
594 "int x; A y(static_cast<int &&>(x)); }");
595 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
596 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
597 ElementsAre("static_cast<int &&>(x)"));
599 AST
= buildASTFromCode("void f() { struct A { A(); A(A&&); }; "
600 "A x; A y(static_cast<A &&>(x)); }");
601 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
602 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
603 ElementsAre("static_cast<A &&>(x)"));
606 TEST(ExprMutationAnalyzerTest
, ByConstRRefArgument
) {
607 auto AST
= buildASTFromCode(
608 "void g(const int&&); void f() { int x; g(static_cast<int&&>(x)); }");
610 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
611 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
612 ElementsAre("static_cast<int &&>(x)"));
614 AST
= buildASTFromCode("struct A {}; A operator+(const A&&, int);"
615 "void f() { A x; static_cast<A&&>(x) + 1; }");
616 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
617 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
618 ElementsAre("static_cast<A &&>(x)"));
620 AST
= buildASTFromCode("void f() { struct A { A(const int&&); }; "
621 "int x; A y(static_cast<int&&>(x)); }");
622 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
623 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
624 ElementsAre("static_cast<int &&>(x)"));
626 AST
= buildASTFromCode("void f() { struct A { A(); A(const A&&); }; "
627 "A x; A y(static_cast<A&&>(x)); }");
628 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
629 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
630 ElementsAre("static_cast<A &&>(x)"));
633 // Section: bindings.
635 TEST(ExprMutationAnalyzerTest
, BindingModifies
) {
637 buildASTFromCode("struct Point { int x; int y; };"
639 "void f(Point p) { auto& [x, y] = p; mod(x); }");
641 match(withEnclosingCompound(declRefTo("p")), AST
->getASTContext());
642 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x", "mod(x)"));
645 TEST(ExprMutationAnalyzerTest
, BindingDoesNotModify
) {
646 const auto AST
= buildASTFromCode("struct Point { int x; int y; };"
647 "void f(Point p) { auto& [x, y] = p; x; }");
649 match(withEnclosingCompound(declRefTo("p")), AST
->getASTContext());
650 EXPECT_FALSE(isMutated(Results
, AST
.get()));
653 // section: explicit std::move and std::forward testing
655 TEST(ExprMutationAnalyzerTest
, Move
) {
656 auto AST
= buildASTFromCode(StdRemoveReference
+ StdMove
+
657 "void f() { struct A {}; A x; std::move(x); }");
659 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
660 EXPECT_FALSE(isMutated(Results
, AST
.get()));
662 AST
= buildASTFromCode(StdRemoveReference
+ StdMove
+
663 "void f() { struct A {}; A x, y; std::move(x) = y; }");
664 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
665 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("std::move(x) = y"));
667 AST
= buildASTFromCode(StdRemoveReference
+ StdMove
+
668 "void f() { int x, y; y = std::move(x); }");
669 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
670 EXPECT_FALSE(isMutated(Results
, AST
.get()));
673 buildASTFromCode(StdRemoveReference
+ StdMove
+
674 "struct S { S(); S(const S&); S& operator=(const S&); };"
675 "void f() { S x, y; y = std::move(x); }");
676 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
677 EXPECT_FALSE(isMutated(Results
, AST
.get()));
679 AST
= buildASTFromCode(StdRemoveReference
+ StdMove
+
680 "struct S { S(); S(S&&); S& operator=(S&&); };"
681 "void f() { S x, y; y = std::move(x); }");
682 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
683 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("y = std::move(x)"));
685 AST
= buildASTFromCode(StdRemoveReference
+ StdMove
+
686 "struct S { S(); S(const S&); S(S&&);"
687 "S& operator=(const S&); S& operator=(S&&); };"
688 "void f() { S x, y; y = std::move(x); }");
689 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
690 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("y = std::move(x)"));
692 AST
= buildASTFromCode(StdRemoveReference
+ StdMove
+
693 "struct S { S(); S(const S&); S(S&&);"
694 "S& operator=(const S&); S& operator=(S&&); };"
695 "void f() { const S x; S y; y = std::move(x); }");
696 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
697 EXPECT_FALSE(isMutated(Results
, AST
.get()));
699 AST
= buildASTFromCode(StdRemoveReference
+ StdMove
+
700 "struct S { S(); S& operator=(S); };"
701 "void f() { S x, y; y = std::move(x); }");
702 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
703 EXPECT_FALSE(isMutated(Results
, AST
.get()));
705 AST
= buildASTFromCode(StdRemoveReference
+ StdMove
+
706 "struct S{}; void f() { S x, y; y = std::move(x); }");
707 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
708 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("y = std::move(x)"));
710 AST
= buildASTFromCode(
711 StdRemoveReference
+ StdMove
+
712 "struct S{}; void f() { const S x; S y; y = std::move(x); }");
713 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
714 EXPECT_FALSE(isMutated(Results
, AST
.get()));
717 TEST(ExprMutationAnalyzerTest
, Forward
) {
719 buildASTFromCode(StdRemoveReference
+ StdForward
+
720 "void f() { struct A {}; A x; std::forward<A &>(x); }");
722 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
723 EXPECT_FALSE(isMutated(Results
, AST
.get()));
725 AST
= buildASTFromCode(
726 StdRemoveReference
+ StdForward
+
727 "void f() { struct A {}; A x, y; std::forward<A &>(x) = y; }");
728 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
729 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
730 ElementsAre("std::forward<A &>(x) = y"));
733 // section: template constellations that prohibit reasoning about modifications
734 // as it depends on instantiations.
736 TEST(ExprMutationAnalyzerTest
, CallUnresolved
) {
738 buildASTFromCodeWithArgs("template <class T> void f() { T x; g(x); }",
739 {"-fno-delayed-template-parsing"});
741 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
742 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
745 buildASTFromCodeWithArgs("template <int N> void f() { char x[N]; g(x); }",
746 {"-fno-delayed-template-parsing"});
747 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
748 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
750 AST
= buildASTFromCodeWithArgs(
751 "template <class T> void f(T t) { int x; g(t, x); }",
752 {"-fno-delayed-template-parsing"});
753 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
754 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(t, x)"));
756 AST
= buildASTFromCodeWithArgs(
757 "template <class T> void f(T t) { int x; t.mf(x); }",
758 {"-fno-delayed-template-parsing"});
759 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
760 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("t.mf(x)"));
762 AST
= buildASTFromCodeWithArgs(
763 "template <class T> struct S;"
764 "template <class T> void f() { S<T> s; int x; s.mf(x); }",
765 {"-fno-delayed-template-parsing"});
766 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
767 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("s.mf(x)"));
769 AST
= buildASTFromCodeWithArgs(
770 "struct S { template <class T> void mf(); };"
771 "template <class T> void f(S s) { int x; s.mf<T>(x); }",
772 {"-fno-delayed-template-parsing"});
773 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
774 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("s.mf<T>(x)"));
776 AST
= buildASTFromCodeWithArgs("template <class F>"
777 "void g(F f) { int x; f(x); } ",
778 {"-fno-delayed-template-parsing"});
779 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
780 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("f(x)"));
782 AST
= buildASTFromCodeWithArgs(
783 "template <class T> void f() { int x; (void)T(x); }",
784 {"-fno-delayed-template-parsing"});
785 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
786 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("T(x)"));
789 // section: return values
791 TEST(ExprMutationAnalyzerTest
, ReturnAsValue
) {
792 auto AST
= buildASTFromCode("int f() { int x; return x; }");
794 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
795 EXPECT_FALSE(isMutated(Results
, AST
.get()));
797 AST
= buildASTFromCode("int* f() { int* x; return x; }");
798 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
799 EXPECT_FALSE(isMutated(Results
, AST
.get()));
801 AST
= buildASTFromCode("typedef int* IntPtr;"
802 "IntPtr f() { int* x; return x; }");
803 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
804 EXPECT_FALSE(isMutated(Results
, AST
.get()));
807 TEST(ExprMutationAnalyzerTest
, ReturnAsNonConstRef
) {
808 const auto AST
= buildASTFromCode("int& f() { int x; return x; }");
810 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
811 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("return x;"));
814 TEST(ExprMutationAnalyzerTest
, ReturnAsConstRef
) {
815 const auto AST
= buildASTFromCode("const int& f() { int x; return x; }");
817 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
818 EXPECT_FALSE(isMutated(Results
, AST
.get()));
821 TEST(ExprMutationAnalyzerTest
, ReturnAsNonConstRRef
) {
823 buildASTFromCode("int&& f() { int x; return static_cast<int &&>(x); }");
825 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
826 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
827 ElementsAre("static_cast<int &&>(x)"));
830 TEST(ExprMutationAnalyzerTest
, ReturnAsConstRRef
) {
831 const auto AST
= buildASTFromCode(
832 "const int&& f() { int x; return static_cast<int&&>(x); }");
834 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
835 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
836 ElementsAre("static_cast<int &&>(x)"));
839 // section: taking the address of a variable and pointers
841 TEST(ExprMutationAnalyzerTest
, TakeAddress
) {
842 const auto AST
= buildASTFromCode("void g(int*); void f() { int x; g(&x); }");
844 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
845 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("&x"));
848 TEST(ExprMutationAnalyzerTest
, ArrayToPointerDecay
) {
850 buildASTFromCode("void g(int*); void f() { int x[2]; g(x); }");
852 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
853 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x"));
856 TEST(ExprMutationAnalyzerTest
, TemplateWithArrayToPointerDecay
) {
857 const auto AST
= buildASTFromCodeWithArgs(
858 "template <typename T> struct S { static constexpr int v = 8; };"
859 "template <> struct S<int> { static constexpr int v = 4; };"
861 "template <typename T> void f() { char x[S<T>::v]; g(x); }"
862 "template <> void f<int>() { char y[S<int>::v]; g(y); }",
863 {"-fno-delayed-template-parsing"});
864 const auto ResultsX
=
865 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
866 EXPECT_THAT(mutatedBy(ResultsX
, AST
.get()), ElementsAre("g(x)"));
867 const auto ResultsY
=
868 match(withEnclosingCompound(declRefTo("y")), AST
->getASTContext());
869 EXPECT_THAT(mutatedBy(ResultsY
, AST
.get()), ElementsAre("y"));
872 // section: special case: all created references are non-mutating themself
873 // and therefore all become 'const'/the value is not modified!
875 TEST(ExprMutationAnalyzerTest
, FollowRefModified
) {
876 auto AST
= buildASTFromCode(
877 "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
878 "int& r3 = r2; r3 = 10; }");
880 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
881 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
882 ElementsAre("r0", "r1", "r2", "r3", "r3 = 10"));
884 AST
= buildASTFromCode("typedef int& IntRefX;"
885 "using IntRefY = int&;"
886 "void f() { int x; IntRefX r0 = x; IntRefY r1 = r0;"
887 "decltype((x)) r2 = r1; r2 = 10; }");
888 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
889 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
890 ElementsAre("r0", "r1", "r2", "r2 = 10"));
893 TEST(ExprMutationAnalyzerTest
, FollowRefNotModified
) {
894 auto AST
= buildASTFromCode(
895 "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; "
896 "int& r3 = r2; int& r4 = r3; int& r5 = r4;}");
898 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
899 EXPECT_FALSE(isMutated(Results
, AST
.get()));
901 AST
= buildASTFromCode("void f() { int x; int& r0 = x; const int& r1 = r0;}");
902 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
903 EXPECT_FALSE(isMutated(Results
, AST
.get()));
905 AST
= buildASTFromCode("typedef const int& CIntRefX;"
906 "using CIntRefY = const int&;"
907 "void f() { int x; int& r0 = x; CIntRefX r1 = r0;"
908 "CIntRefY r2 = r1; decltype((r1)) r3 = r2;}");
909 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
910 EXPECT_FALSE(isMutated(Results
, AST
.get()));
913 TEST(ExprMutationAnalyzerTest
, FollowConditionalRefModified
) {
914 const auto AST
= buildASTFromCode(
915 "void f() { int x, y; bool b; int &r = b ? x : y; r = 10; }");
917 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
918 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("r", "r = 10"));
921 TEST(ExprMutationAnalyzerTest
, FollowConditionalRefNotModified
) {
923 buildASTFromCode("void f() { int x, y; bool b; int& r = b ? x : y; }");
925 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
926 EXPECT_FALSE(isMutated(Results
, AST
.get()));
929 TEST(ExprMutationAnalyzerTest
, FollowFuncArgModified
) {
930 auto AST
= buildASTFromCode("template <class T> void g(T&& t) { t = 10; }"
931 "void f() { int x; g(x); }");
933 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
934 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
936 AST
= buildASTFromCode(
938 "template <class... Args> void g(Args&&... args) { h(args...); }"
939 "void f() { int x; g(x); }");
940 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
941 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
943 AST
= buildASTFromCode(
945 "template <class... Args> void g(Args&&... args) { h(args...); }"
946 "void f() { int x, y; g(x, y); }");
947 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
948 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x, y)"));
949 Results
= match(withEnclosingCompound(declRefTo("y")), AST
->getASTContext());
950 EXPECT_FALSE(isMutated(Results
, AST
.get()));
952 AST
= buildASTFromCode(
954 "template <class... Args> void g(Args&&... args) { h(args...); }"
955 "void f() { int x, y; g(y, x); }");
956 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
957 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(y, x)"));
958 Results
= match(withEnclosingCompound(declRefTo("y")), AST
->getASTContext());
959 EXPECT_FALSE(isMutated(Results
, AST
.get()));
961 AST
= buildASTFromCode("struct S { template <class T> S(T&& t) { t = 10; } };"
962 "void f() { int x; S s(x); }");
963 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
964 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x"));
966 AST
= buildASTFromCode(
967 "struct S { template <class T> S(T&& t) : m(++t) { } int m; };"
968 "void f() { int x; S s(x); }");
969 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
970 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x"));
972 AST
= buildASTFromCode("template <class U> struct S {"
973 "template <class T> S(T&& t) : m(++t) { } U m; };"
974 "void f() { int x; S<int> s(x); }");
975 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
976 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x"));
978 AST
= buildASTFromCode(StdRemoveReference
+ StdForward
+
979 "template <class... Args> void u(Args&...);"
980 "template <class... Args> void h(Args&&... args)"
981 "{ u(std::forward<Args>(args)...); }"
982 "template <class... Args> void g(Args&&... args)"
983 "{ h(std::forward<Args>(args)...); }"
984 "void f() { int x; g(x); }");
985 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
986 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("g(x)"));
988 AST
= buildASTFromCode(
989 StdRemoveReference
+ StdForward
+
990 "template <class T> void f1(T &&a);"
991 "template <class T> void f2(T &&a);"
992 "template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); }"
993 "template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); }"
994 "void f() { int x; f1(x); }");
995 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
996 EXPECT_FALSE(isMutated(Results
, AST
.get()));
998 AST
= buildASTFromCode(
999 StdRemoveReference
+ StdForward
+
1000 "template <class T> void f1(T &&a);"
1001 "template <class T> void f2(T &&a);"
1002 "template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); }"
1003 "template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); a++; }"
1004 "void f() { int x; f1(x); }");
1005 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1006 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("f1(x)"));
1008 AST
= buildASTFromCode(
1009 StdRemoveReference
+ StdForward
+
1010 "template <class T> void f1(T &&a);"
1011 "template <class T> void f2(T &&a);"
1012 "template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); a++; }"
1013 "template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); }"
1014 "void f() { int x; f1(x); }");
1015 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1016 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("f1(x)"));
1019 TEST(ExprMutationAnalyzerTest
, FollowFuncArgNotModified
) {
1020 auto AST
= buildASTFromCode("template <class T> void g(T&&) {}"
1021 "void f() { int x; g(x); }");
1023 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1024 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1026 AST
= buildASTFromCode("template <class T> void g(T&& t) { t; }"
1027 "void f() { int x; g(x); }");
1028 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1029 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1031 AST
= buildASTFromCode("template <class... Args> void g(Args&&...) {}"
1032 "void f() { int x; g(x); }");
1033 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1034 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1036 AST
= buildASTFromCode("template <class... Args> void g(Args&&...) {}"
1037 "void f() { int y, x; g(y, x); }");
1038 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1039 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1041 AST
= buildASTFromCode(
1042 "void h(int, int&);"
1043 "template <class... Args> void g(Args&&... args) { h(args...); }"
1044 "void f() { int x, y; g(x, y); }");
1045 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1046 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1048 AST
= buildASTFromCode("struct S { template <class T> S(T&& t) { t; } };"
1049 "void f() { int x; S s(x); }");
1050 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1051 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1053 AST
= buildASTFromCode(
1054 "struct S { template <class T> S(T&& t) : m(t) { } int m; };"
1055 "void f() { int x; S s(x); }");
1056 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1057 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1059 AST
= buildASTFromCode("template <class U> struct S {"
1060 "template <class T> S(T&& t) : m(t) { } U m; };"
1061 "void f() { int x; S<int> s(x); }");
1062 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1063 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1065 AST
= buildASTFromCode(StdRemoveReference
+ StdForward
+
1066 "template <class... Args> void u(Args...);"
1067 "template <class... Args> void h(Args&&... args)"
1068 "{ u(std::forward<Args>(args)...); }"
1069 "template <class... Args> void g(Args&&... args)"
1070 "{ h(std::forward<Args>(args)...); }"
1071 "void f() { int x; g(x); }");
1072 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1073 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1076 // section: builtin arrays
1078 TEST(ExprMutationAnalyzerTest
, ArrayElementModified
) {
1079 const auto AST
= buildASTFromCode("void f() { int x[2]; x[0] = 10; }");
1080 const auto Results
=
1081 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1082 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x[0] = 10"));
1085 TEST(ExprMutationAnalyzerTest
, ArrayElementNotModified
) {
1086 const auto AST
= buildASTFromCode("void f() { int x[2]; x[0]; }");
1087 const auto Results
=
1088 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1089 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1092 // section: member modifications
1094 TEST(ExprMutationAnalyzerTest
, NestedMemberModified
) {
1096 buildASTFromCode("void f() { struct A { int vi; }; struct B { A va; }; "
1097 "struct C { B vb; }; C x; x.vb.va.vi = 10; }");
1099 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1100 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.vb.va.vi = 10"));
1102 AST
= buildASTFromCodeWithArgs(
1103 "template <class T> void f() { T x; x.y.z = 10; }",
1104 {"-fno-delayed-template-parsing"});
1105 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1106 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.y.z = 10"));
1108 AST
= buildASTFromCodeWithArgs(
1109 "template <class T> struct S;"
1110 "template <class T> void f() { S<T> x; x.y.z = 10; }",
1111 {"-fno-delayed-template-parsing"});
1112 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1113 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.y.z = 10"));
1116 TEST(ExprMutationAnalyzerTest
, NestedMemberNotModified
) {
1118 buildASTFromCode("void f() { struct A { int vi; }; struct B { A va; }; "
1119 "struct C { B vb; }; C x; x.vb.va.vi; }");
1121 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1122 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1124 AST
= buildASTFromCodeWithArgs("template <class T> void f() { T x; x.y.z; }",
1125 {"-fno-delayed-template-parsing"});
1126 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1127 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1130 buildASTFromCodeWithArgs("template <class T> struct S;"
1131 "template <class T> void f() { S<T> x; x.y.z; }",
1132 {"-fno-delayed-template-parsing"});
1133 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1134 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1139 TEST(ExprMutationAnalyzerTest
, CastToValue
) {
1141 buildASTFromCode("void f() { int x; static_cast<double>(x); }");
1142 const auto Results
=
1143 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1144 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1147 TEST(ExprMutationAnalyzerTest
, CastToRefModified
) {
1149 buildASTFromCode("void f() { int x; static_cast<int &>(x) = 10; }");
1151 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1152 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
1153 ElementsAre("static_cast<int &>(x)"));
1155 AST
= buildASTFromCode("typedef int& IntRef;"
1156 "void f() { int x; static_cast<IntRef>(x) = 10; }");
1157 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1158 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
1159 ElementsAre("static_cast<IntRef>(x)"));
1162 TEST(ExprMutationAnalyzerTest
, CastToRefNotModified
) {
1164 buildASTFromCode("void f() { int x; static_cast<int&>(x); }");
1165 const auto Results
=
1166 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1167 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
1168 ElementsAre("static_cast<int &>(x)"));
1171 TEST(ExprMutationAnalyzerTest
, CastToConstRef
) {
1173 buildASTFromCode("void f() { int x; static_cast<const int&>(x); }");
1175 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1176 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1178 AST
= buildASTFromCode("typedef const int& CIntRef;"
1179 "void f() { int x; static_cast<CIntRef>(x); }");
1180 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1181 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1184 // section: comma expressions
1186 TEST(ExprMutationAnalyzerTest
, CommaExprWithAnAssignment
) {
1187 const auto AST
= buildASTFromCodeWithArgs(
1188 "void f() { int x; int y; (x, y) = 5; }", {"-Wno-unused-value"});
1189 const auto Results
=
1190 match(withEnclosingCompound(declRefTo("y")), AST
->getASTContext());
1191 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1194 TEST(ExprMutationAnalyzerTest
, CommaExprWithDecOp
) {
1195 const auto AST
= buildASTFromCodeWithArgs(
1196 "void f() { int x; int y; (x, y)++; }", {"-Wno-unused-value"});
1197 const auto Results
=
1198 match(withEnclosingCompound(declRefTo("y")), AST
->getASTContext());
1199 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1202 TEST(ExprMutationAnalyzerTest
, CommaExprWithNonConstMemberCall
) {
1203 const auto AST
= buildASTFromCodeWithArgs(
1204 "class A { public: int mem; void f() { mem ++; } };"
1205 "void fn() { A o1, o2; (o1, o2).f(); }",
1206 {"-Wno-unused-value"});
1207 const auto Results
=
1208 match(withEnclosingCompound(declRefTo("o2")), AST
->getASTContext());
1209 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1212 TEST(ExprMutationAnalyzerTest
, CommaExprWithConstMemberCall
) {
1213 const auto AST
= buildASTFromCodeWithArgs(
1214 "class A { public: int mem; void f() const { } };"
1215 "void fn() { A o1, o2; (o1, o2).f(); }",
1216 {"-Wno-unused-value"});
1217 const auto Results
=
1218 match(withEnclosingCompound(declRefTo("o2")), AST
->getASTContext());
1219 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1222 TEST(ExprMutationAnalyzerTest
, CommaExprWithCallExpr
) {
1224 buildASTFromCodeWithArgs("class A { public: int mem; void f(A &O1) {} };"
1225 "void fn() { A o1, o2; o2.f((o2, o1)); }",
1226 {"-Wno-unused-value"});
1227 const auto Results
=
1228 match(withEnclosingCompound(declRefTo("o1")), AST
->getASTContext());
1229 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1232 TEST(ExprMutationAnalyzerTest
, CommaExprWithCallUnresolved
) {
1233 auto AST
= buildASTFromCodeWithArgs(
1234 "template <class T> struct S;"
1235 "template <class T> void f() { S<T> s; int x, y; s.mf((y, x)); }",
1236 {"-fno-delayed-template-parsing", "-Wno-unused-value"});
1238 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1239 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1241 AST
= buildASTFromCodeWithArgs(
1242 "template <class T> void f(T t) { int x, y; g(t, (y, x)); }",
1243 {"-fno-delayed-template-parsing", "-Wno-unused-value"});
1244 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1245 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1248 TEST(ExprMutationAnalyzerTest
, CommaExprParmRef
) {
1250 buildASTFromCodeWithArgs("class A { public: int mem;};"
1251 "extern void fn(A &o1);"
1252 "void fn2 () { A o1, o2; fn((o2, o1)); } ",
1253 {"-Wno-unused-value"});
1254 const auto Results
=
1255 match(withEnclosingCompound(declRefTo("o1")), AST
->getASTContext());
1256 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1259 TEST(ExprMutationAnalyzerTest
, CommaExprWithAmpersandOp
) {
1260 const auto AST
= buildASTFromCodeWithArgs("class A { public: int mem;};"
1261 "void fn () { A o1, o2;"
1262 "void *addr = &(o2, o1); } ",
1263 {"-Wno-unused-value"});
1264 const auto Results
=
1265 match(withEnclosingCompound(declRefTo("o1")), AST
->getASTContext());
1266 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1269 TEST(ExprMutationAnalyzerTest
, CommaExprAsReturnAsValue
) {
1270 auto AST
= buildASTFromCodeWithArgs("int f() { int x, y; return (x, y); }",
1271 {"-Wno-unused-value"});
1273 match(withEnclosingCompound(declRefTo("y")), AST
->getASTContext());
1274 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1277 TEST(ExprMutationAnalyzerTest
, CommaExprAsReturnAsNonConstRef
) {
1278 const auto AST
= buildASTFromCodeWithArgs(
1279 "int& f() { int x, y; return (y, x); }", {"-Wno-unused-value"});
1280 const auto Results
=
1281 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1282 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1285 TEST(ExprMutationAnalyzerTest
, CommaExprAsArrayToPointerDecay
) {
1287 buildASTFromCodeWithArgs("void g(int*); "
1288 "void f() { int x[2], y[2]; g((y, x)); }",
1289 {"-Wno-unused-value"});
1290 const auto Results
=
1291 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1292 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1295 TEST(ExprMutationAnalyzerTest
, CommaExprAsUniquePtr
) {
1296 const std::string UniquePtrDef
= "template <class T> struct UniquePtr {"
1298 " UniquePtr(const UniquePtr&) = delete;"
1299 " T& operator*() const;"
1300 " T* operator->() const;"
1302 const auto AST
= buildASTFromCodeWithArgs(
1303 UniquePtrDef
+ "template <class T> void f() "
1304 "{ UniquePtr<T> x; UniquePtr<T> y;"
1306 {"-fno-delayed-template-parsing", "-Wno-unused-value"});
1307 const auto Results
=
1308 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1309 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1312 TEST(ExprMutationAnalyzerTest
, CommaNestedConditional
) {
1313 const std::string Code
= "void f() { int x, y = 42;"
1314 " y, (true ? x : y) = 42; }";
1315 const auto AST
= buildASTFromCodeWithArgs(Code
, {"-Wno-unused-value"});
1316 const auto Results
=
1317 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1318 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
1319 ElementsAre("(true ? x : y) = 42"));
1322 // section: lambda captures
1324 TEST(ExprMutationAnalyzerTest
, LambdaDefaultCaptureByValue
) {
1325 const auto AST
= buildASTFromCode("void f() { int x; [=]() { x; }; }");
1326 const auto Results
=
1327 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1328 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1331 TEST(ExprMutationAnalyzerTest
, LambdaExplicitCaptureByValue
) {
1332 const auto AST
= buildASTFromCode("void f() { int x; [x]() { x; }; }");
1333 const auto Results
=
1334 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1335 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1338 TEST(ExprMutationAnalyzerTest
, LambdaDefaultCaptureByRef
) {
1339 const auto AST
= buildASTFromCode("void f() { int x; [&]() { x = 10; }; }");
1340 const auto Results
=
1341 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1342 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
1343 ElementsAre(ResultOf(removeSpace
, "[&](){x=10;}")));
1346 TEST(ExprMutationAnalyzerTest
, LambdaExplicitCaptureByRef
) {
1347 const auto AST
= buildASTFromCode("void f() { int x; [&x]() { x = 10; }; }");
1348 const auto Results
=
1349 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1350 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
1351 ElementsAre(ResultOf(removeSpace
, "[&x](){x=10;}")));
1354 // section: range-for loops
1356 TEST(ExprMutationAnalyzerTest
, RangeForArrayByRefModified
) {
1358 buildASTFromCode("void f() { int x[2]; for (int& e : x) e = 10; }");
1360 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1361 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
1362 ElementsAre("for (int &e : x)\n e = 10;"));
1364 AST
= buildASTFromCode("typedef int& IntRef;"
1365 "void f() { int x[2]; for (IntRef e : x) e = 10; }");
1366 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1367 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
1368 ElementsAre("for (IntRef e : x)\n e = 10;"));
1371 TEST(ExprMutationAnalyzerTest
, RangeForArrayByRefModifiedByImplicitInit
) {
1373 buildASTFromCode("void f() { int x[2]; for (int& e : x) e; }");
1374 const auto Results
=
1375 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1376 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1379 TEST(ExprMutationAnalyzerTest
, RangeForArrayByValue
) {
1380 auto AST
= buildASTFromCode("void f() { int x[2]; for (int e : x) e = 10; }");
1382 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1383 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1386 buildASTFromCode("void f() { int* x[2]; for (int* e : x) e = nullptr; }");
1387 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1388 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1390 AST
= buildASTFromCode(
1391 "typedef int* IntPtr;"
1392 "void f() { int* x[2]; for (IntPtr e : x) e = nullptr; }");
1393 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1394 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1397 TEST(ExprMutationAnalyzerTest
, RangeForArrayByConstRef
) {
1399 buildASTFromCode("void f() { int x[2]; for (const int& e : x) e; }");
1401 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1402 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1404 AST
= buildASTFromCode("typedef const int& CIntRef;"
1405 "void f() { int x[2]; for (CIntRef e : x) e; }");
1406 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1407 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1410 TEST(ExprMutationAnalyzerTest
, RangeForNonArrayByRefModified
) {
1412 buildASTFromCode("struct V { int* begin(); int* end(); };"
1413 "void f() { V x; for (int& e : x) e = 10; }");
1414 const auto Results
=
1415 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1416 EXPECT_THAT(mutatedBy(Results
, AST
.get()),
1417 ElementsAre("for (int &e : x)\n e = 10;"));
1420 TEST(ExprMutationAnalyzerTest
, RangeForNonArrayByRefNotModified
) {
1421 const auto AST
= buildASTFromCode("struct V { int* begin(); int* end(); };"
1422 "void f() { V x; for (int& e : x) e; }");
1423 const auto Results
=
1424 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1425 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1428 TEST(ExprMutationAnalyzerTest
, RangeForNonArrayByValue
) {
1429 const auto AST
= buildASTFromCode(
1430 "struct V { const int* begin() const; const int* end() const; };"
1431 "void f() { V x; for (int e : x) e; }");
1432 const auto Results
=
1433 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1434 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1437 TEST(ExprMutationAnalyzerTest
, RangeForNonArrayByConstRef
) {
1438 const auto AST
= buildASTFromCode(
1439 "struct V { const int* begin() const; const int* end() const; };"
1440 "void f() { V x; for (const int& e : x) e; }");
1441 const auto Results
=
1442 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1443 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1446 // section: unevaluated expressions
1448 TEST(ExprMutationAnalyzerTest
, UnevaluatedExpressions
) {
1449 auto AST
= buildASTFromCode("void f() { int x, y; decltype(x = 10) z = y; }");
1451 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1452 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1454 AST
= buildASTFromCode("void f() { int x, y; __typeof(x = 10) z = y; }");
1455 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1456 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1458 AST
= buildASTFromCode("void f() { int x, y; __typeof__(x = 10) z = y; }");
1459 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1460 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1462 AST
= buildASTFromCode("void f() { int x; sizeof(x = 10); }");
1463 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1464 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1466 AST
= buildASTFromCode("void f() { int x; alignof(x = 10); }");
1467 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1468 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1470 AST
= buildASTFromCode("void f() { int x; noexcept(x = 10); }");
1471 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1472 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1474 AST
= buildASTFromCodeWithArgs("namespace std { class type_info; }"
1475 "void f() { int x; typeid(x = 10); }",
1477 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1478 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1480 AST
= buildASTFromCode(
1481 "void f() { int x; _Generic(x = 10, int: 0, default: 1); }");
1482 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1483 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1486 TEST(ExprMutationAnalyzerTest
, NotUnevaluatedExpressions
) {
1487 auto AST
= buildASTFromCode("void f() { int x; sizeof(int[x++]); }");
1489 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1490 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x++"));
1492 AST
= buildASTFromCodeWithArgs(
1493 "namespace std { class type_info; }"
1494 "struct A { virtual ~A(); }; struct B : A {};"
1495 "struct X { A& f(); }; void f() { X x; typeid(x.f()); }",
1497 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1498 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x.f()"));
1501 // section: special case: smartpointers
1503 TEST(ExprMutationAnalyzerTest
, UniquePtr
) {
1504 const std::string UniquePtrDef
=
1505 "template <class T> struct UniquePtr {"
1507 " UniquePtr(const UniquePtr&) = delete;"
1508 " UniquePtr(UniquePtr&&);"
1509 " UniquePtr& operator=(const UniquePtr&) = delete;"
1510 " UniquePtr& operator=(UniquePtr&&);"
1511 " T& operator*() const;"
1512 " T* operator->() const;"
1515 auto AST
= buildASTFromCode(UniquePtrDef
+
1516 "void f() { UniquePtr<int> x; *x = 10; }");
1518 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1519 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("* x = 10"));
1521 AST
= buildASTFromCode(UniquePtrDef
+ "void f() { UniquePtr<int> x; *x; }");
1522 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1523 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1525 AST
= buildASTFromCode(UniquePtrDef
+
1526 "void f() { UniquePtr<const int> x; *x; }");
1527 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1528 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1530 AST
= buildASTFromCode(UniquePtrDef
+ "struct S { int v; };"
1531 "void f() { UniquePtr<S> x; x->v; }");
1532 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1533 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x"));
1535 AST
= buildASTFromCode(UniquePtrDef
+
1536 "struct S { int v; };"
1537 "void f() { UniquePtr<const S> x; x->v; }");
1538 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1539 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1542 buildASTFromCode(UniquePtrDef
+ "struct S { void mf(); };"
1543 "void f() { UniquePtr<S> x; x->mf(); }");
1544 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1545 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x"));
1547 AST
= buildASTFromCode(UniquePtrDef
+
1548 "struct S { void mf() const; };"
1549 "void f() { UniquePtr<const S> x; x->mf(); }");
1550 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1551 EXPECT_FALSE(isMutated(Results
, AST
.get()));
1553 AST
= buildASTFromCodeWithArgs(
1554 UniquePtrDef
+ "template <class T> void f() { UniquePtr<T> x; x->mf(); }",
1555 {"-fno-delayed-template-parsing"});
1556 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1557 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("x->mf()"));
1560 // section: complex problems detected on real code
1562 TEST(ExprMutationAnalyzerTest
, SelfRef
) {
1563 std::unique_ptr
<ASTUnit
> AST
{};
1564 SmallVector
<BoundNodes
, 1> Results
{};
1566 AST
= buildASTFromCodeWithArgs("void f() { int &x = x; }",
1567 {"-Wno-unused-value", "-Wno-uninitialized"});
1568 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1569 EXPECT_FALSE(isDeclMutated(Results
, AST
.get()));
1571 AST
= buildASTFromCodeWithArgs("void f() { int &x = x; x = 1; }",
1572 {"-Wno-unused-value", "-Wno-uninitialized"});
1573 Results
= match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1574 EXPECT_TRUE(isDeclMutated(Results
, AST
.get()));
1577 TEST(ExprMutationAnalyzerTest
, UnevaluatedContext
) {
1578 const std::string Example
=
1579 "template <typename T>"
1580 "struct to_construct : T { to_construct(int &j) {} };"
1581 "template <typename T>"
1582 "void placement_new_in_unique_ptr() { int x = 0;"
1583 " new to_construct<T>(x);"
1586 buildASTFromCodeWithArgs(Example
, {"-fno-delayed-template-parsing"});
1588 match(withEnclosingCompound(declRefTo("x")), AST
->getASTContext());
1589 EXPECT_TRUE(isMutated(Results
, AST
.get()));
1590 EXPECT_THAT(mutatedBy(Results
, AST
.get()), ElementsAre("(x)"));
1593 TEST(ExprMutationAnalyzerTest
, ReproduceFailureMinimal
) {
1594 const std::string Reproducer
=
1596 "template <class T> T &forward(T &A) { return static_cast<T&&>(A); }"
1597 "template <class T> struct __bind {"
1599 " template <class V> __bind(T v, V &&) : f(forward(v)) {}"
1604 " auto Lambda = [] {};"
1605 " std::__bind<decltype(Lambda)>(Lambda, x);"
1607 auto AST11
= buildASTFromCodeWithArgs(Reproducer
, {"-std=c++11"});
1609 match(withEnclosingCompound(declRefTo("x")), AST11
->getASTContext());
1610 EXPECT_FALSE(isMutated(Results11
, AST11
.get()));
1612 } // namespace clang