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