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/Frontend/ASTUnit.h" 14 #include "clang/Tooling/Tooling.h" 15 #include "gmock/gmock.h" 16 #include "gtest/gtest.h" 17 #include <cassert> 18 #include <cctype> 19 20 namespace clang { 21 22 using namespace clang::ast_matchers; 23 using ::testing::ElementsAre; 24 using ::testing::ResultOf; 25 using ::testing::Values; 26 27 namespace { 28 29 using ExprMatcher = internal::Matcher<Expr>; 30 using StmtMatcher = internal::Matcher<Stmt>; 31 32 std::unique_ptr<ASTUnit> 33 buildASTFromCodeWithArgs(const Twine &Code, 34 const std::vector<std::string> &Args) { 35 SmallString<1024> CodeStorage; 36 auto AST = 37 tooling::buildASTFromCodeWithArgs(Code.toStringRef(CodeStorage), Args); 38 EXPECT_FALSE(AST->getDiagnostics().hasErrorOccurred()); 39 return AST; 40 } 41 42 std::unique_ptr<ASTUnit> buildASTFromCode(const Twine &Code) { 43 return buildASTFromCodeWithArgs(Code, {}); 44 } 45 46 ExprMatcher declRefTo(StringRef Name) { 47 return declRefExpr(to(namedDecl(hasName(Name)).bind("decl"))); 48 } 49 50 StmtMatcher withEnclosingCompound(ExprMatcher Matcher) { 51 return expr(Matcher, hasAncestor(compoundStmt().bind("stmt"))).bind("expr"); 52 } 53 54 bool isMutated(const SmallVectorImpl<BoundNodes> &Results, ASTUnit *AST) { 55 const auto *const S = selectFirst<Stmt>("stmt", Results); 56 const auto *const E = selectFirst<Expr>("expr", Results); 57 TraversalKindScope RAII(AST->getASTContext(), TK_AsIs); 58 return ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(E); 59 } 60 61 bool isDeclMutated(const SmallVectorImpl<BoundNodes> &Results, ASTUnit *AST) { 62 const auto *const S = selectFirst<Stmt>("stmt", Results); 63 const auto *const D = selectFirst<Decl>("decl", Results); 64 TraversalKindScope RAII(AST->getASTContext(), TK_AsIs); 65 return ExprMutationAnalyzer(*S, AST->getASTContext()).isMutated(D); 66 } 67 68 SmallVector<std::string, 1> 69 mutatedBy(const SmallVectorImpl<BoundNodes> &Results, ASTUnit *AST) { 70 const auto *const S = selectFirst<Stmt>("stmt", Results); 71 SmallVector<std::string, 1> Chain; 72 ExprMutationAnalyzer Analyzer(*S, AST->getASTContext()); 73 74 for (const auto *E = selectFirst<Expr>("expr", Results); E != nullptr;) { 75 const Stmt *By = Analyzer.findMutation(E); 76 if (!By) 77 break; 78 79 std::string Buffer; 80 llvm::raw_string_ostream Stream(Buffer); 81 By->printPretty(Stream, nullptr, AST->getASTContext().getPrintingPolicy()); 82 Chain.emplace_back(StringRef(Buffer).trim().str()); 83 E = dyn_cast<DeclRefExpr>(By); 84 } 85 return Chain; 86 } 87 88 std::string removeSpace(std::string s) { 89 s.erase(std::remove_if(s.begin(), s.end(), 90 [](char c) { return llvm::isSpace(c); }), 91 s.end()); 92 return s; 93 } 94 95 const std::string StdRemoveReference = 96 "namespace std {" 97 "template<class T> struct remove_reference { typedef T type; };" 98 "template<class T> struct remove_reference<T&> { typedef T type; };" 99 "template<class T> struct remove_reference<T&&> { typedef T type; }; }"; 100 101 const std::string StdMove = 102 "namespace std {" 103 "template<class T> typename remove_reference<T>::type&& " 104 "move(T&& t) noexcept {" 105 "return static_cast<typename remove_reference<T>::type&&>(t); } }"; 106 107 const std::string StdForward = 108 "namespace std {" 109 "template<class T> T&& " 110 "forward(typename remove_reference<T>::type& t) noexcept { return t; }" 111 "template<class T> T&& " 112 "forward(typename remove_reference<T>::type&& t) noexcept { return t; } }"; 113 114 } // namespace 115 116 TEST(ExprMutationAnalyzerTest, Trivial) { 117 const auto AST = buildASTFromCode("void f() { int x; x; }"); 118 const auto Results = 119 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 120 EXPECT_FALSE(isMutated(Results, AST.get())); 121 } 122 123 class AssignmentTest : public ::testing::TestWithParam<std::string> {}; 124 125 // This test is for the most basic and direct modification of a variable, 126 // assignment to it (e.g. `x = 10;`). 127 // It additionally tests that references to a variable are not only captured 128 // directly but expressions that result in the variable are handled, too. 129 // This includes the comma operator, parens and the ternary operator. 130 TEST_P(AssignmentTest, AssignmentModifies) { 131 // Test the detection of the raw expression modifications. 132 { 133 const std::string ModExpr = "x " + GetParam() + " 10"; 134 const auto AST = buildASTFromCode("void f() { int x; " + ModExpr + "; }"); 135 const auto Results = 136 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 137 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); 138 } 139 140 // Test the detection if the expression is surrounded by parens. 141 { 142 const std::string ModExpr = "(x) " + GetParam() + " 10"; 143 const auto AST = buildASTFromCode("void f() { int x; " + ModExpr + "; }"); 144 const auto Results = 145 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 146 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); 147 } 148 149 // Test the detection if the comma operator yields the expression as result. 150 { 151 const std::string ModExpr = "x " + GetParam() + " 10"; 152 const auto AST = buildASTFromCodeWithArgs( 153 "void f() { int x, y; y, " + ModExpr + "; }", {"-Wno-unused-value"}); 154 const auto Results = 155 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 156 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); 157 } 158 159 // Ensure no detection if the comma operator does not yield the expression as 160 // result. 161 { 162 const std::string ModExpr = "y, x, y " + GetParam() + " 10"; 163 const auto AST = buildASTFromCodeWithArgs( 164 "void f() { int x, y; " + ModExpr + "; }", {"-Wno-unused-value"}); 165 const auto Results = 166 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 167 EXPECT_FALSE(isMutated(Results, AST.get())); 168 } 169 170 // Test the detection if the a ternary operator can result in the expression. 171 { 172 const std::string ModExpr = "(y != 0 ? y : x) " + GetParam() + " 10"; 173 const auto AST = 174 buildASTFromCode("void f() { int y = 0, x; " + ModExpr + "; }"); 175 const auto Results = 176 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 177 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); 178 } 179 180 // Test the detection if the a ternary operator can result in the expression 181 // through multiple nesting of ternary operators. 182 { 183 const std::string ModExpr = 184 "(y != 0 ? (y > 5 ? y : x) : (y)) " + GetParam() + " 10"; 185 const auto AST = 186 buildASTFromCode("void f() { int y = 0, x; " + ModExpr + "; }"); 187 const auto Results = 188 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 189 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); 190 } 191 192 // Test the detection if the a ternary operator can result in the expression 193 // with additional parens. 194 { 195 const std::string ModExpr = "(y != 0 ? (y) : ((x))) " + GetParam() + " 10"; 196 const auto AST = 197 buildASTFromCode("void f() { int y = 0, x; " + ModExpr + "; }"); 198 const auto Results = 199 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 200 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); 201 } 202 203 // Test the detection for the binary conditional operator. 204 { 205 const std::string ModExpr = "(y ?: x) " + GetParam() + " 10"; 206 const auto AST = 207 buildASTFromCode("void f() { int y = 0, x; " + ModExpr + "; }"); 208 const auto Results = 209 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 210 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); 211 } 212 } 213 214 INSTANTIATE_TEST_SUITE_P(AllAssignmentOperators, AssignmentTest, 215 Values("=", "+=", "-=", "*=", "/=", "%=", "&=", "|=", 216 "^=", "<<=", ">>=") ); 217 218 TEST(ExprMutationAnalyzerTest, AssignmentConditionalWithInheritance) { 219 const auto AST = buildASTFromCode("struct Base {void nonconst(); };" 220 "struct Derived : Base {};" 221 "static void f() {" 222 " Derived x, y;" 223 " Base &b = true ? x : y;" 224 " b.nonconst();" 225 "}"); 226 const auto Results = 227 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 228 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("b", "b.nonconst()")); 229 } 230 231 class IncDecTest : public ::testing::TestWithParam<std::string> {}; 232 233 TEST_P(IncDecTest, IncDecModifies) { 234 const std::string ModExpr = GetParam(); 235 const auto AST = buildASTFromCode("void f() { int x; " + ModExpr + "; }"); 236 const auto Results = 237 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 238 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre(ModExpr)); 239 } 240 241 INSTANTIATE_TEST_SUITE_P(AllIncDecOperators, IncDecTest, 242 Values("++x", "--x", "x++", "x--", "++(x)", "--(x)", 243 "(x)++", "(x)--") ); 244 245 // Section: member functions 246 247 TEST(ExprMutationAnalyzerTest, NonConstMemberFunc) { 248 const auto AST = buildASTFromCode( 249 "void f() { struct Foo { void mf(); }; Foo x; x.mf(); }"); 250 const auto Results = 251 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 252 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); 253 } 254 255 TEST(ExprMutationAnalyzerTest, AssumedNonConstMemberFunc) { 256 auto AST = buildASTFromCodeWithArgs( 257 "struct X { template <class T> void mf(); };" 258 "template <class T> void f() { X x; x.mf<T>(); }", 259 {"-fno-delayed-template-parsing"}); 260 auto Results = 261 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 262 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf<T>()")); 263 264 AST = buildASTFromCodeWithArgs("template <class T> void f() { T x; x.mf(); }", 265 {"-fno-delayed-template-parsing"}); 266 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 267 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); 268 269 AST = buildASTFromCodeWithArgs( 270 "template <class T> struct X;" 271 "template <class T> void f() { X<T> x; x.mf(); }", 272 {"-fno-delayed-template-parsing"}); 273 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 274 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.mf()")); 275 } 276 277 TEST(ExprMutationAnalyzerTest, ConstMemberFunc) { 278 const auto AST = buildASTFromCode( 279 "void f() { struct Foo { void mf() const; }; Foo x; x.mf(); }"); 280 const auto Results = 281 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 282 EXPECT_FALSE(isMutated(Results, AST.get())); 283 } 284 285 TEST(ExprMutationAnalyzerTest, TypeDependentMemberCall) { 286 const auto AST = buildASTFromCodeWithArgs( 287 "template <class T> class vector { void push_back(T); }; " 288 "template <class T> void f() { vector<T> x; x.push_back(T()); }", 289 {"-fno-delayed-template-parsing"}); 290 const auto Results = 291 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 292 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.push_back(T())")); 293 } 294 295 TEST(ExprMutationAnalyzerTest, MemberPointerMemberCall) { 296 { 297 const auto AST = 298 buildASTFromCode("struct X {};" 299 "using T = int (X::*)();" 300 "void f(X &x, T m) { X &ref = x; (ref.*m)(); }"); 301 const auto Results = 302 match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext()); 303 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(ref .* m)()")); 304 } 305 { 306 const auto AST = 307 buildASTFromCode("struct X {};" 308 "using T = int (X::*)();" 309 "void f(X &x, T const m) { X &ref = x; (ref.*m)(); }"); 310 const auto Results = 311 match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext()); 312 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(ref .* m)()")); 313 } 314 { 315 const auto AST = 316 buildASTFromCode("struct X {};" 317 "using T = int (X::*)() const;" 318 "void f(X &x, T m) { X &ref = x; (ref.*m)(); }"); 319 const auto Results = 320 match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext()); 321 EXPECT_FALSE(isMutated(Results, AST.get())); 322 } 323 } 324 325 // Section: overloaded operators 326 327 TEST(ExprMutationAnalyzerTest, NonConstOperator) { 328 const auto AST = buildASTFromCode( 329 "void f() { struct Foo { Foo& operator=(int); }; Foo x; x = 10; }"); 330 const auto Results = 331 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 332 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x = 10")); 333 } 334 335 TEST(ExprMutationAnalyzerTest, ConstOperator) { 336 const auto AST = buildASTFromCode( 337 "void f() { struct Foo { int operator()() const; }; Foo x; x(); }"); 338 const auto Results = 339 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 340 EXPECT_FALSE(isMutated(Results, AST.get())); 341 } 342 343 TEST(ExprMutationAnalyzerTest, UnresolvedOperator) { 344 const auto AST = buildASTFromCodeWithArgs( 345 "template <typename Stream> void input_operator_template() {" 346 "Stream x; unsigned y = 42;" 347 "x >> y; }", 348 {"-fno-delayed-template-parsing"}); 349 const auto Results = 350 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 351 EXPECT_TRUE(isMutated(Results, AST.get())); 352 } 353 354 TEST(ExprMutationAnalyzerTest, DependentOperatorWithNonDependentOperand) { 355 // gh57297 356 // The expression to check may not be the dependent operand in a dependent 357 // operator. 358 359 // Explicitly not declaring a (templated) stream operator 360 // so the `<<` is a `binaryOperator` with a dependent type. 361 const auto AST = buildASTFromCodeWithArgs( 362 "struct Stream { };" 363 "template <typename T> void f() { T t; Stream x; x << t; }", 364 {"-fno-delayed-template-parsing"}); 365 const auto Results = 366 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 367 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x << t")); 368 } 369 370 TEST(ExprMutationAnalyzerTest, FoldExpression) { 371 // gh70323 372 // A fold expression may contain `Exp` as it's initializer. 373 // We don't know if the operator modifies `Exp` because the 374 // operator is type dependent due to the parameter pack. 375 auto AST = buildASTFromCodeWithArgs( 376 "struct Stream {};" 377 "template <typename... Args> void concatenate(Args... args) " 378 "{ Stream x; (x << ... << args); }", 379 {"-fno-delayed-template-parsing"}); 380 auto Results = 381 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 382 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(x << ... << args)")); 383 384 AST = buildASTFromCodeWithArgs( 385 "struct Stream {};" 386 "template <typename... Args> void concatenate(Args... args) " 387 "{ Stream x; (args << ... << x); }", 388 {"-fno-delayed-template-parsing"}); 389 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 390 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(args << ... << x)")); 391 392 AST = buildASTFromCodeWithArgs( 393 "struct Stream {};" 394 "template <typename... Args> void concatenate(Args... args) " 395 "{ Stream x; (..., (x << args)); }", 396 {"-fno-delayed-template-parsing"}); 397 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 398 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x << args")); 399 } 400 401 // Section: expression as call argument 402 403 TEST(ExprMutationAnalyzerTest, ByValueArgument) { 404 auto AST = buildASTFromCode("void g(int); void f() { int x; g(x); }"); 405 auto Results = 406 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 407 EXPECT_FALSE(isMutated(Results, AST.get())); 408 409 AST = buildASTFromCode("void g(int*); void f() { int* x; g(x); }"); 410 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 411 EXPECT_FALSE(isMutated(Results, AST.get())); 412 413 AST = buildASTFromCode("typedef int* IntPtr;" 414 "void g(IntPtr); void f() { int* x; g(x); }"); 415 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 416 EXPECT_FALSE(isMutated(Results, AST.get())); 417 418 AST = buildASTFromCode( 419 "struct A {}; A operator+(A, int); void f() { A x; x + 1; }"); 420 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 421 EXPECT_FALSE(isMutated(Results, AST.get())); 422 423 AST = buildASTFromCode("void f() { struct A { A(int); }; int x; A y(x); }"); 424 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 425 EXPECT_FALSE(isMutated(Results, AST.get())); 426 427 AST = buildASTFromCode("struct A { A(); A& operator=(A); };" 428 "void f() { A x, y; y = x; }"); 429 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 430 EXPECT_FALSE(isMutated(Results, AST.get())); 431 432 AST = buildASTFromCode( 433 "template <int> struct A { A(); A(const A&); static void mf(A) {} };" 434 "void f() { A<0> x; A<0>::mf(x); }"); 435 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 436 EXPECT_FALSE(isMutated(Results, AST.get())); 437 } 438 439 TEST(ExprMutationAnalyzerTest, ByConstValueArgument) { 440 auto AST = buildASTFromCode("void g(const int); void f() { int x; g(x); }"); 441 auto Results = 442 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 443 EXPECT_FALSE(isMutated(Results, AST.get())); 444 445 AST = buildASTFromCode("void g(int* const); void f() { int* x; g(x); }"); 446 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 447 EXPECT_FALSE(isMutated(Results, AST.get())); 448 449 AST = buildASTFromCode("typedef int* const CIntPtr;" 450 "void g(CIntPtr); void f() { int* x; g(x); }"); 451 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 452 EXPECT_FALSE(isMutated(Results, AST.get())); 453 454 AST = buildASTFromCode( 455 "struct A {}; A operator+(const A, int); void f() { A x; x + 1; }"); 456 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 457 EXPECT_FALSE(isMutated(Results, AST.get())); 458 459 AST = buildASTFromCode( 460 "void f() { struct A { A(const int); }; int x; A y(x); }"); 461 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 462 EXPECT_FALSE(isMutated(Results, AST.get())); 463 464 AST = buildASTFromCode("template <int> struct A { A(); A(const A&);" 465 "static void mf(const A&) {} };" 466 "void f() { A<0> x; A<0>::mf(x); }"); 467 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 468 EXPECT_FALSE(isMutated(Results, AST.get())); 469 } 470 471 TEST(ExprMutationAnalyzerTest, ByNonConstRefArgument) { 472 auto AST = buildASTFromCode("void g(int&); void f() { int x; g(x); }"); 473 auto Results = 474 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 475 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); 476 477 AST = buildASTFromCode("typedef int& IntRef;" 478 "void g(IntRef); void f() { int x; g(x); }"); 479 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 480 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); 481 482 AST = buildASTFromCode("template <class T> using TRef = T&;" 483 "void g(TRef<int>); void f() { int x; g(x); }"); 484 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 485 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); 486 487 AST = buildASTFromCode( 488 "template <class T> struct identity { using type = T; };" 489 "template <class T, class U = T&> void g(typename identity<U>::type);" 490 "void f() { int x; g<int>(x); }"); 491 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 492 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g<int>(x)")); 493 494 AST = buildASTFromCode("typedef int* IntPtr;" 495 "void g(IntPtr&); void f() { int* x; g(x); }"); 496 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 497 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); 498 499 AST = buildASTFromCode("typedef int* IntPtr; typedef IntPtr& IntPtrRef;" 500 "void g(IntPtrRef); void f() { int* x; g(x); }"); 501 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 502 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); 503 504 AST = buildASTFromCode( 505 "struct A {}; A operator+(A&, int); void f() { A x; x + 1; }"); 506 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 507 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x + 1")); 508 509 AST = buildASTFromCode("void f() { struct A { A(int&); }; int x; A y(x); }"); 510 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 511 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); 512 513 AST = buildASTFromCode("void f() { struct A { A(); A(A&); }; A x; A y(x); }"); 514 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 515 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); 516 517 AST = buildASTFromCode( 518 "template <int> struct A { A(); A(const A&); static void mf(A&) {} };" 519 "void f() { A<0> x; A<0>::mf(x); }"); 520 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 521 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("A<0>::mf(x)")); 522 } 523 524 TEST(ExprMutationAnalyzerTest, ByNonConstRefArgumentFunctionTypeDependent) { 525 auto AST = buildASTFromCodeWithArgs( 526 "enum MyEnum { foo, bar };" 527 "void tryParser(unsigned& first, MyEnum Type) { first++, (void)Type; }" 528 "template <MyEnum Type> void parse() {" 529 " auto parser = [](unsigned& first) { first++; tryParser(first, Type); " 530 "};" 531 " unsigned x = 42;" 532 " parser(x);" 533 "}", 534 {"-fno-delayed-template-parsing"}); 535 auto Results = 536 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 537 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("parser(x)")); 538 } 539 540 TEST(ExprMutationAnalyzerTest, ByConstRefArgument) { 541 auto AST = buildASTFromCode("void g(const int&); void f() { int x; g(x); }"); 542 auto Results = 543 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 544 EXPECT_FALSE(isMutated(Results, AST.get())); 545 546 AST = buildASTFromCode("typedef const int& CIntRef;" 547 "void g(CIntRef); void f() { int x; g(x); }"); 548 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 549 EXPECT_FALSE(isMutated(Results, AST.get())); 550 551 AST = buildASTFromCode("template <class T> using CTRef = const T&;" 552 "void g(CTRef<int>); void f() { int x; g(x); }"); 553 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 554 EXPECT_FALSE(isMutated(Results, AST.get())); 555 556 AST = 557 buildASTFromCode("template <class T> struct identity { using type = T; };" 558 "template <class T, class U = const T&>" 559 "void g(typename identity<U>::type);" 560 "void f() { int x; g<int>(x); }"); 561 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 562 EXPECT_FALSE(isMutated(Results, AST.get())); 563 564 AST = buildASTFromCode( 565 "struct A {}; A operator+(const A&, int); void f() { A x; x + 1; }"); 566 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 567 EXPECT_FALSE(isMutated(Results, AST.get())); 568 569 AST = buildASTFromCode( 570 "void f() { struct A { A(const int&); }; int x; A y(x); }"); 571 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 572 EXPECT_FALSE(isMutated(Results, AST.get())); 573 574 AST = buildASTFromCode( 575 "void f() { struct A { A(); A(const A&); }; A x; A y(x); }"); 576 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 577 EXPECT_FALSE(isMutated(Results, AST.get())); 578 } 579 580 TEST(ExprMutationAnalyzerTest, ByNonConstRRefArgument) { 581 auto AST = buildASTFromCode( 582 "void g(int&&); void f() { int x; g(static_cast<int &&>(x)); }"); 583 auto Results = 584 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 585 EXPECT_THAT(mutatedBy(Results, AST.get()), 586 ElementsAre("g(static_cast<int &&>(x))")); 587 588 AST = buildASTFromCode("struct A {}; A operator+(A&&, int);" 589 "void f() { A x; static_cast<A &&>(x) + 1; }"); 590 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 591 EXPECT_THAT(mutatedBy(Results, AST.get()), 592 ElementsAre("static_cast<A &&>(x) + 1")); 593 594 AST = buildASTFromCode("void f() { struct A { A(int&&); }; " 595 "int x; A y(static_cast<int &&>(x)); }"); 596 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 597 EXPECT_THAT(mutatedBy(Results, AST.get()), 598 ElementsAre("static_cast<int &&>(x)")); 599 600 AST = buildASTFromCode("void f() { struct A { A(); A(A&&); }; " 601 "A x; A y(static_cast<A &&>(x)); }"); 602 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 603 EXPECT_THAT(mutatedBy(Results, AST.get()), 604 ElementsAre("static_cast<A &&>(x)")); 605 } 606 607 TEST(ExprMutationAnalyzerTest, ByConstRRefArgument) { 608 auto AST = buildASTFromCode( 609 "void g(const int&&); void f() { int x; g(static_cast<int&&>(x)); }"); 610 auto Results = 611 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 612 EXPECT_THAT(mutatedBy(Results, AST.get()), 613 ElementsAre("static_cast<int &&>(x)")); 614 615 AST = buildASTFromCode("struct A {}; A operator+(const A&&, int);" 616 "void f() { A x; static_cast<A&&>(x) + 1; }"); 617 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 618 EXPECT_THAT(mutatedBy(Results, AST.get()), 619 ElementsAre("static_cast<A &&>(x)")); 620 621 AST = buildASTFromCode("void f() { struct A { A(const int&&); }; " 622 "int x; A y(static_cast<int&&>(x)); }"); 623 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 624 EXPECT_THAT(mutatedBy(Results, AST.get()), 625 ElementsAre("static_cast<int &&>(x)")); 626 627 AST = buildASTFromCode("void f() { struct A { A(); A(const A&&); }; " 628 "A x; A y(static_cast<A&&>(x)); }"); 629 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 630 EXPECT_THAT(mutatedBy(Results, AST.get()), 631 ElementsAre("static_cast<A &&>(x)")); 632 } 633 634 // Section: bindings. 635 636 TEST(ExprMutationAnalyzerTest, BindingModifies) { 637 const auto AST = 638 buildASTFromCode("struct Point { int x; int y; };" 639 "void mod(int&);" 640 "void f(Point p) { auto& [x, y] = p; mod(x); }"); 641 const auto Results = 642 match(withEnclosingCompound(declRefTo("p")), AST->getASTContext()); 643 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x", "mod(x)")); 644 } 645 646 TEST(ExprMutationAnalyzerTest, BindingDoesNotModify) { 647 const auto AST = buildASTFromCode("struct Point { int x; int y; };" 648 "void f(Point p) { auto& [x, y] = p; x; }"); 649 const auto Results = 650 match(withEnclosingCompound(declRefTo("p")), AST->getASTContext()); 651 EXPECT_FALSE(isMutated(Results, AST.get())); 652 } 653 654 // section: explicit std::move and std::forward testing 655 656 TEST(ExprMutationAnalyzerTest, Move) { 657 auto AST = buildASTFromCode(StdRemoveReference + StdMove + 658 "void f() { struct A {}; A x; std::move(x); }"); 659 auto Results = 660 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 661 EXPECT_FALSE(isMutated(Results, AST.get())); 662 663 AST = buildASTFromCode(StdRemoveReference + StdMove + 664 "void f() { struct A {}; A x, y; std::move(x) = y; }"); 665 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 666 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("std::move(x) = y")); 667 668 AST = buildASTFromCode(StdRemoveReference + StdMove + 669 "void f() { int x, y; y = std::move(x); }"); 670 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 671 EXPECT_FALSE(isMutated(Results, AST.get())); 672 673 AST = 674 buildASTFromCode(StdRemoveReference + StdMove + 675 "struct S { S(); S(const S&); S& operator=(const S&); };" 676 "void f() { S x, y; y = std::move(x); }"); 677 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 678 EXPECT_FALSE(isMutated(Results, AST.get())); 679 680 AST = buildASTFromCode(StdRemoveReference + StdMove + 681 "struct S { S(); S(S&&); S& operator=(S&&); };" 682 "void f() { S x, y; y = std::move(x); }"); 683 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 684 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)")); 685 686 AST = buildASTFromCode(StdRemoveReference + StdMove + 687 "struct S { S(); S(const S&); S(S&&);" 688 "S& operator=(const S&); S& operator=(S&&); };" 689 "void f() { S x, y; y = std::move(x); }"); 690 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 691 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)")); 692 693 AST = buildASTFromCode(StdRemoveReference + StdMove + 694 "struct S { S(); S(const S&); S(S&&);" 695 "S& operator=(const S&); S& operator=(S&&); };" 696 "void f() { const S x; S y; y = std::move(x); }"); 697 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 698 EXPECT_FALSE(isMutated(Results, AST.get())); 699 700 AST = buildASTFromCode(StdRemoveReference + StdMove + 701 "struct S { S(); S& operator=(S); };" 702 "void f() { S x, y; y = std::move(x); }"); 703 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 704 EXPECT_FALSE(isMutated(Results, AST.get())); 705 706 AST = buildASTFromCode(StdRemoveReference + StdMove + 707 "struct S{}; void f() { S x, y; y = std::move(x); }"); 708 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 709 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("y = std::move(x)")); 710 711 AST = buildASTFromCode( 712 StdRemoveReference + StdMove + 713 "struct S{}; void f() { const S x; S y; y = std::move(x); }"); 714 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 715 EXPECT_FALSE(isMutated(Results, AST.get())); 716 } 717 718 TEST(ExprMutationAnalyzerTest, Forward) { 719 auto AST = 720 buildASTFromCode(StdRemoveReference + StdForward + 721 "void f() { struct A {}; A x; std::forward<A &>(x); }"); 722 auto Results = 723 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 724 EXPECT_FALSE(isMutated(Results, AST.get())); 725 726 AST = buildASTFromCode( 727 StdRemoveReference + StdForward + 728 "void f() { struct A {}; A x, y; std::forward<A &>(x) = y; }"); 729 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 730 EXPECT_THAT(mutatedBy(Results, AST.get()), 731 ElementsAre("std::forward<A &>(x) = y")); 732 } 733 734 // section: template constellations that prohibit reasoning about modifications 735 // as it depends on instantiations. 736 737 TEST(ExprMutationAnalyzerTest, CallUnresolved) { 738 auto AST = 739 buildASTFromCodeWithArgs("template <class T> void f() { T x; g(x); }", 740 {"-fno-delayed-template-parsing"}); 741 auto Results = 742 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 743 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); 744 745 AST = 746 buildASTFromCodeWithArgs("template <int N> void f() { char x[N]; g(x); }", 747 {"-fno-delayed-template-parsing"}); 748 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 749 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); 750 751 AST = buildASTFromCodeWithArgs( 752 "template <class T> void f(T t) { int x; g(t, x); }", 753 {"-fno-delayed-template-parsing"}); 754 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 755 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(t, x)")); 756 757 AST = buildASTFromCodeWithArgs( 758 "template <class T> void f(T t) { int x; t.mf(x); }", 759 {"-fno-delayed-template-parsing"}); 760 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 761 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("t.mf(x)")); 762 763 AST = buildASTFromCodeWithArgs( 764 "template <class T> struct S;" 765 "template <class T> void f() { S<T> s; int x; s.mf(x); }", 766 {"-fno-delayed-template-parsing"}); 767 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 768 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf(x)")); 769 770 AST = buildASTFromCodeWithArgs( 771 "struct S { template <class T> void mf(); };" 772 "template <class T> void f(S s) { int x; s.mf<T>(x); }", 773 {"-fno-delayed-template-parsing"}); 774 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 775 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("s.mf<T>(x)")); 776 777 AST = buildASTFromCodeWithArgs("template <class F>" 778 "void g(F f) { int x; f(x); } ", 779 {"-fno-delayed-template-parsing"}); 780 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 781 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f(x)")); 782 783 AST = buildASTFromCodeWithArgs( 784 "template <class T> void f() { int x; (void)T(x); }", 785 {"-fno-delayed-template-parsing"}); 786 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 787 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("T(x)")); 788 } 789 790 // section: return values 791 792 TEST(ExprMutationAnalyzerTest, ReturnAsValue) { 793 auto AST = buildASTFromCode("int f() { int x; return x; }"); 794 auto Results = 795 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 796 EXPECT_FALSE(isMutated(Results, AST.get())); 797 798 AST = buildASTFromCode("int* f() { int* x; return x; }"); 799 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 800 EXPECT_FALSE(isMutated(Results, AST.get())); 801 802 AST = buildASTFromCode("typedef int* IntPtr;" 803 "IntPtr f() { int* x; return x; }"); 804 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 805 EXPECT_FALSE(isMutated(Results, AST.get())); 806 } 807 808 TEST(ExprMutationAnalyzerTest, ReturnAsNonConstRef) { 809 const auto AST = buildASTFromCode("int& f() { int x; return x; }"); 810 const auto Results = 811 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 812 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("return x;")); 813 } 814 815 TEST(ExprMutationAnalyzerTest, ReturnAsConstRef) { 816 const auto AST = buildASTFromCode("const int& f() { int x; return x; }"); 817 const auto Results = 818 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 819 EXPECT_FALSE(isMutated(Results, AST.get())); 820 } 821 822 TEST(ExprMutationAnalyzerTest, ReturnAsNonConstRRef) { 823 const auto AST = 824 buildASTFromCode("int&& f() { int x; return static_cast<int &&>(x); }"); 825 const auto Results = 826 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 827 EXPECT_THAT(mutatedBy(Results, AST.get()), 828 ElementsAre("static_cast<int &&>(x)")); 829 } 830 831 TEST(ExprMutationAnalyzerTest, ReturnAsConstRRef) { 832 const auto AST = buildASTFromCode( 833 "const int&& f() { int x; return static_cast<int&&>(x); }"); 834 const auto Results = 835 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 836 EXPECT_THAT(mutatedBy(Results, AST.get()), 837 ElementsAre("static_cast<int &&>(x)")); 838 } 839 840 // section: taking the address of a variable and pointers 841 842 TEST(ExprMutationAnalyzerTest, TakeAddress) { 843 const auto AST = buildASTFromCode("void g(int*); void f() { int x; 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, ArrayToPointerDecay) { 850 const auto AST = 851 buildASTFromCode("void g(int*); void f() { int x[2]; g(x); }"); 852 const auto Results = 853 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 854 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); 855 } 856 857 TEST(ExprMutationAnalyzerTest, TemplateWithArrayToPointerDecay) { 858 const auto AST = buildASTFromCodeWithArgs( 859 "template <typename T> struct S { static constexpr int v = 8; };" 860 "template <> struct S<int> { static constexpr int v = 4; };" 861 "void g(char*);" 862 "template <typename T> void f() { char x[S<T>::v]; g(x); }" 863 "template <> void f<int>() { char y[S<int>::v]; g(y); }", 864 {"-fno-delayed-template-parsing"}); 865 const auto ResultsX = 866 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 867 EXPECT_THAT(mutatedBy(ResultsX, AST.get()), ElementsAre("g(x)")); 868 const auto ResultsY = 869 match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); 870 EXPECT_THAT(mutatedBy(ResultsY, AST.get()), ElementsAre("y")); 871 } 872 873 // section: special case: all created references are non-mutating themself 874 // and therefore all become 'const'/the value is not modified! 875 876 TEST(ExprMutationAnalyzerTest, FollowRefModified) { 877 auto AST = buildASTFromCode( 878 "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; " 879 "int& r3 = r2; r3 = 10; }"); 880 auto Results = 881 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 882 EXPECT_THAT(mutatedBy(Results, AST.get()), 883 ElementsAre("r0", "r1", "r2", "r3", "r3 = 10")); 884 885 AST = buildASTFromCode("typedef int& IntRefX;" 886 "using IntRefY = int&;" 887 "void f() { int x; IntRefX r0 = x; IntRefY r1 = r0;" 888 "decltype((x)) r2 = r1; r2 = 10; }"); 889 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 890 EXPECT_THAT(mutatedBy(Results, AST.get()), 891 ElementsAre("r0", "r1", "r2", "r2 = 10")); 892 } 893 894 TEST(ExprMutationAnalyzerTest, FollowRefNotModified) { 895 auto AST = buildASTFromCode( 896 "void f() { int x; int& r0 = x; int& r1 = r0; int& r2 = r1; " 897 "int& r3 = r2; int& r4 = r3; int& r5 = r4;}"); 898 auto Results = 899 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 900 EXPECT_FALSE(isMutated(Results, AST.get())); 901 902 AST = buildASTFromCode("void f() { int x; int& r0 = x; const int& r1 = r0;}"); 903 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 904 EXPECT_FALSE(isMutated(Results, AST.get())); 905 906 AST = buildASTFromCode("typedef const int& CIntRefX;" 907 "using CIntRefY = const int&;" 908 "void f() { int x; int& r0 = x; CIntRefX r1 = r0;" 909 "CIntRefY r2 = r1; decltype((r1)) r3 = r2;}"); 910 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 911 EXPECT_FALSE(isMutated(Results, AST.get())); 912 } 913 914 TEST(ExprMutationAnalyzerTest, FollowConditionalRefModified) { 915 const auto AST = buildASTFromCode( 916 "void f() { int x, y; bool b; int &r = b ? x : y; r = 10; }"); 917 const auto Results = 918 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 919 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("r", "r = 10")); 920 } 921 922 TEST(ExprMutationAnalyzerTest, FollowConditionalRefNotModified) { 923 const auto AST = 924 buildASTFromCode("void f() { int x, y; bool b; int& r = b ? x : y; }"); 925 const auto Results = 926 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 927 EXPECT_FALSE(isMutated(Results, AST.get())); 928 } 929 930 TEST(ExprMutationAnalyzerTest, FollowFuncArgModified) { 931 auto AST = buildASTFromCode("template <class T> void g(T&& t) { t = 10; }" 932 "void f() { int x; g(x); }"); 933 auto Results = 934 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 935 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); 936 937 AST = buildASTFromCode( 938 "void h(int&);" 939 "template <class... Args> void g(Args&&... args) { h(args...); }" 940 "void f() { int x; g(x); }"); 941 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 942 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); 943 944 AST = buildASTFromCode( 945 "void h(int&, int);" 946 "template <class... Args> void g(Args&&... args) { h(args...); }" 947 "void f() { int x, y; g(x, y); }"); 948 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 949 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x, y)")); 950 Results = match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); 951 EXPECT_FALSE(isMutated(Results, AST.get())); 952 953 AST = buildASTFromCode( 954 "void h(int, int&);" 955 "template <class... Args> void g(Args&&... args) { h(args...); }" 956 "void f() { int x, y; g(y, x); }"); 957 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 958 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(y, x)")); 959 Results = match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); 960 EXPECT_FALSE(isMutated(Results, AST.get())); 961 962 AST = buildASTFromCode("struct S { template <class T> S(T&& t) { t = 10; } };" 963 "void f() { int x; S s(x); }"); 964 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 965 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); 966 967 AST = buildASTFromCode( 968 "struct S { template <class T> S(T&& t) : m(++t) { } int m; };" 969 "void f() { int x; S s(x); }"); 970 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 971 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); 972 973 AST = buildASTFromCode("template <class U> struct S {" 974 "template <class T> S(T&& t) : m(++t) { } U m; };" 975 "void f() { int x; S<int> s(x); }"); 976 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 977 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); 978 979 AST = buildASTFromCode(StdRemoveReference + StdForward + 980 "template <class... Args> void u(Args&...);" 981 "template <class... Args> void h(Args&&... args)" 982 "{ u(std::forward<Args>(args)...); }" 983 "template <class... Args> void g(Args&&... args)" 984 "{ h(std::forward<Args>(args)...); }" 985 "void f() { int x; g(x); }"); 986 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 987 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("g(x)")); 988 989 AST = buildASTFromCode( 990 StdRemoveReference + StdForward + 991 "template <class T> void f1(T &&a);" 992 "template <class T> void f2(T &&a);" 993 "template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); }" 994 "template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); }" 995 "void f() { int x; f1(x); }"); 996 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 997 EXPECT_FALSE(isMutated(Results, AST.get())); 998 999 AST = buildASTFromCode( 1000 StdRemoveReference + StdForward + 1001 "template <class T> void f1(T &&a);" 1002 "template <class T> void f2(T &&a);" 1003 "template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); }" 1004 "template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); a++; }" 1005 "void f() { int x; f1(x); }"); 1006 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1007 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f1(x)")); 1008 1009 AST = buildASTFromCode( 1010 StdRemoveReference + StdForward + 1011 "template <class T> void f1(T &&a);" 1012 "template <class T> void f2(T &&a);" 1013 "template <class T> void f1(T &&a) { f2<T>(std::forward<T>(a)); a++; }" 1014 "template <class T> void f2(T &&a) { f1<T>(std::forward<T>(a)); }" 1015 "void f() { int x; f1(x); }"); 1016 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1017 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("f1(x)")); 1018 } 1019 1020 TEST(ExprMutationAnalyzerTest, FollowFuncArgNotModified) { 1021 auto AST = buildASTFromCode("template <class T> void g(T&&) {}" 1022 "void f() { int x; g(x); }"); 1023 auto Results = 1024 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1025 EXPECT_FALSE(isMutated(Results, AST.get())); 1026 1027 AST = buildASTFromCode("template <class T> void g(T&& t) { t; }" 1028 "void f() { int x; g(x); }"); 1029 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1030 EXPECT_FALSE(isMutated(Results, AST.get())); 1031 1032 AST = buildASTFromCode("template <class... Args> void g(Args&&...) {}" 1033 "void f() { int x; g(x); }"); 1034 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1035 EXPECT_FALSE(isMutated(Results, AST.get())); 1036 1037 AST = buildASTFromCode("template <class... Args> void g(Args&&...) {}" 1038 "void f() { int y, x; g(y, x); }"); 1039 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1040 EXPECT_FALSE(isMutated(Results, AST.get())); 1041 1042 AST = buildASTFromCode( 1043 "void h(int, int&);" 1044 "template <class... Args> void g(Args&&... args) { h(args...); }" 1045 "void f() { int x, y; g(x, y); }"); 1046 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1047 EXPECT_FALSE(isMutated(Results, AST.get())); 1048 1049 AST = buildASTFromCode("struct S { template <class T> S(T&& t) { t; } };" 1050 "void f() { int x; S s(x); }"); 1051 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1052 EXPECT_FALSE(isMutated(Results, AST.get())); 1053 1054 AST = buildASTFromCode( 1055 "struct S { template <class T> S(T&& t) : m(t) { } int m; };" 1056 "void f() { int x; S s(x); }"); 1057 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1058 EXPECT_FALSE(isMutated(Results, AST.get())); 1059 1060 AST = buildASTFromCode("template <class U> struct S {" 1061 "template <class T> S(T&& t) : m(t) { } U m; };" 1062 "void f() { int x; S<int> s(x); }"); 1063 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1064 EXPECT_FALSE(isMutated(Results, AST.get())); 1065 1066 AST = buildASTFromCode(StdRemoveReference + StdForward + 1067 "template <class... Args> void u(Args...);" 1068 "template <class... Args> void h(Args&&... args)" 1069 "{ u(std::forward<Args>(args)...); }" 1070 "template <class... Args> void g(Args&&... args)" 1071 "{ h(std::forward<Args>(args)...); }" 1072 "void f() { int x; g(x); }"); 1073 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1074 EXPECT_FALSE(isMutated(Results, AST.get())); 1075 } 1076 1077 // section: builtin arrays 1078 1079 TEST(ExprMutationAnalyzerTest, ArrayElementModified) { 1080 const auto AST = buildASTFromCode("void f() { int x[2]; x[0] = 10; }"); 1081 const auto Results = 1082 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1083 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x[0] = 10")); 1084 } 1085 1086 TEST(ExprMutationAnalyzerTest, ArrayElementNotModified) { 1087 const auto AST = buildASTFromCode("void f() { int x[2]; x[0]; }"); 1088 const auto Results = 1089 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1090 EXPECT_FALSE(isMutated(Results, AST.get())); 1091 } 1092 1093 // section: member modifications 1094 1095 TEST(ExprMutationAnalyzerTest, NestedMemberModified) { 1096 auto AST = 1097 buildASTFromCode("void f() { struct A { int vi; }; struct B { A va; }; " 1098 "struct C { B vb; }; C x; x.vb.va.vi = 10; }"); 1099 auto Results = 1100 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1101 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.vb.va.vi = 10")); 1102 1103 AST = buildASTFromCodeWithArgs( 1104 "template <class T> void f() { T x; x.y.z = 10; }", 1105 {"-fno-delayed-template-parsing"}); 1106 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1107 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10")); 1108 1109 AST = buildASTFromCodeWithArgs( 1110 "template <class T> struct S;" 1111 "template <class T> void f() { S<T> x; x.y.z = 10; }", 1112 {"-fno-delayed-template-parsing"}); 1113 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1114 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.y.z = 10")); 1115 } 1116 1117 TEST(ExprMutationAnalyzerTest, NestedMemberNotModified) { 1118 auto AST = 1119 buildASTFromCode("void f() { struct A { int vi; }; struct B { A va; }; " 1120 "struct C { B vb; }; C x; x.vb.va.vi; }"); 1121 auto Results = 1122 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1123 EXPECT_FALSE(isMutated(Results, AST.get())); 1124 1125 AST = buildASTFromCodeWithArgs("template <class T> void f() { T x; x.y.z; }", 1126 {"-fno-delayed-template-parsing"}); 1127 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1128 EXPECT_FALSE(isMutated(Results, AST.get())); 1129 1130 AST = 1131 buildASTFromCodeWithArgs("template <class T> struct S;" 1132 "template <class T> void f() { S<T> x; x.y.z; }", 1133 {"-fno-delayed-template-parsing"}); 1134 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1135 EXPECT_FALSE(isMutated(Results, AST.get())); 1136 } 1137 1138 // section: casts 1139 1140 TEST(ExprMutationAnalyzerTest, CastToValue) { 1141 const auto AST = 1142 buildASTFromCode("void f() { int x; static_cast<double>(x); }"); 1143 const auto Results = 1144 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1145 EXPECT_FALSE(isMutated(Results, AST.get())); 1146 } 1147 1148 TEST(ExprMutationAnalyzerTest, CastToRefModified) { 1149 auto AST = 1150 buildASTFromCode("void f() { int x; static_cast<int &>(x) = 10; }"); 1151 auto Results = 1152 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1153 EXPECT_THAT(mutatedBy(Results, AST.get()), 1154 ElementsAre("static_cast<int &>(x)")); 1155 1156 AST = buildASTFromCode("typedef int& IntRef;" 1157 "void f() { int x; static_cast<IntRef>(x) = 10; }"); 1158 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1159 EXPECT_THAT(mutatedBy(Results, AST.get()), 1160 ElementsAre("static_cast<IntRef>(x)")); 1161 } 1162 1163 TEST(ExprMutationAnalyzerTest, CastToRefNotModified) { 1164 const auto AST = 1165 buildASTFromCode("void f() { int x; static_cast<int&>(x); }"); 1166 const auto Results = 1167 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1168 EXPECT_THAT(mutatedBy(Results, AST.get()), 1169 ElementsAre("static_cast<int &>(x)")); 1170 } 1171 1172 TEST(ExprMutationAnalyzerTest, CastToConstRef) { 1173 auto AST = 1174 buildASTFromCode("void f() { int x; static_cast<const int&>(x); }"); 1175 auto Results = 1176 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1177 EXPECT_FALSE(isMutated(Results, AST.get())); 1178 1179 AST = buildASTFromCode("typedef const int& CIntRef;" 1180 "void f() { int x; static_cast<CIntRef>(x); }"); 1181 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1182 EXPECT_FALSE(isMutated(Results, AST.get())); 1183 } 1184 1185 // section: comma expressions 1186 1187 TEST(ExprMutationAnalyzerTest, CommaExprWithAnAssignment) { 1188 const auto AST = buildASTFromCodeWithArgs( 1189 "void f() { int x; int y; (x, y) = 5; }", {"-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, CommaExprWithDecOp) { 1196 const auto AST = buildASTFromCodeWithArgs( 1197 "void f() { int x; int y; (x, y)++; }", {"-Wno-unused-value"}); 1198 const auto Results = 1199 match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); 1200 EXPECT_TRUE(isMutated(Results, AST.get())); 1201 } 1202 1203 TEST(ExprMutationAnalyzerTest, CommaExprWithNonConstMemberCall) { 1204 const auto AST = buildASTFromCodeWithArgs( 1205 "class A { public: int mem; void f() { mem ++; } };" 1206 "void fn() { A o1, o2; (o1, o2).f(); }", 1207 {"-Wno-unused-value"}); 1208 const auto Results = 1209 match(withEnclosingCompound(declRefTo("o2")), AST->getASTContext()); 1210 EXPECT_TRUE(isMutated(Results, AST.get())); 1211 } 1212 1213 TEST(ExprMutationAnalyzerTest, CommaExprWithConstMemberCall) { 1214 const auto AST = buildASTFromCodeWithArgs( 1215 "class A { public: int mem; void f() const { } };" 1216 "void fn() { A o1, o2; (o1, o2).f(); }", 1217 {"-Wno-unused-value"}); 1218 const auto Results = 1219 match(withEnclosingCompound(declRefTo("o2")), AST->getASTContext()); 1220 EXPECT_FALSE(isMutated(Results, AST.get())); 1221 } 1222 1223 TEST(ExprMutationAnalyzerTest, CommaExprWithCallExpr) { 1224 const auto AST = 1225 buildASTFromCodeWithArgs("class A { public: int mem; void f(A &O1) {} };" 1226 "void fn() { A o1, o2; o2.f((o2, o1)); }", 1227 {"-Wno-unused-value"}); 1228 const auto Results = 1229 match(withEnclosingCompound(declRefTo("o1")), AST->getASTContext()); 1230 EXPECT_TRUE(isMutated(Results, AST.get())); 1231 } 1232 1233 TEST(ExprMutationAnalyzerTest, CommaExprWithCallUnresolved) { 1234 auto AST = buildASTFromCodeWithArgs( 1235 "template <class T> struct S;" 1236 "template <class T> void f() { S<T> s; int x, y; s.mf((y, x)); }", 1237 {"-fno-delayed-template-parsing", "-Wno-unused-value"}); 1238 auto Results = 1239 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1240 EXPECT_TRUE(isMutated(Results, AST.get())); 1241 1242 AST = buildASTFromCodeWithArgs( 1243 "template <class T> void f(T t) { int x, y; g(t, (y, x)); }", 1244 {"-fno-delayed-template-parsing", "-Wno-unused-value"}); 1245 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1246 EXPECT_TRUE(isMutated(Results, AST.get())); 1247 } 1248 1249 TEST(ExprMutationAnalyzerTest, CommaExprParmRef) { 1250 const auto AST = 1251 buildASTFromCodeWithArgs("class A { public: int mem;};" 1252 "extern void fn(A &o1);" 1253 "void fn2 () { A o1, o2; fn((o2, o1)); } ", 1254 {"-Wno-unused-value"}); 1255 const auto Results = 1256 match(withEnclosingCompound(declRefTo("o1")), AST->getASTContext()); 1257 EXPECT_TRUE(isMutated(Results, AST.get())); 1258 } 1259 1260 TEST(ExprMutationAnalyzerTest, CommaExprWithAmpersandOp) { 1261 const auto AST = buildASTFromCodeWithArgs("class A { public: int mem;};" 1262 "void fn () { A o1, o2;" 1263 "void *addr = &(o2, o1); } ", 1264 {"-Wno-unused-value"}); 1265 const auto Results = 1266 match(withEnclosingCompound(declRefTo("o1")), AST->getASTContext()); 1267 EXPECT_TRUE(isMutated(Results, AST.get())); 1268 } 1269 1270 TEST(ExprMutationAnalyzerTest, CommaExprAsReturnAsValue) { 1271 auto AST = buildASTFromCodeWithArgs("int f() { int x, y; return (x, y); }", 1272 {"-Wno-unused-value"}); 1273 auto Results = 1274 match(withEnclosingCompound(declRefTo("y")), AST->getASTContext()); 1275 EXPECT_FALSE(isMutated(Results, AST.get())); 1276 } 1277 1278 TEST(ExprMutationAnalyzerTest, CommaExprAsReturnAsNonConstRef) { 1279 const auto AST = buildASTFromCodeWithArgs( 1280 "int& f() { int x, y; return (y, x); }", {"-Wno-unused-value"}); 1281 const auto Results = 1282 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1283 EXPECT_TRUE(isMutated(Results, AST.get())); 1284 } 1285 1286 TEST(ExprMutationAnalyzerTest, CommaExprAsArrayToPointerDecay) { 1287 const auto AST = 1288 buildASTFromCodeWithArgs("void g(int*); " 1289 "void f() { int x[2], y[2]; g((y, x)); }", 1290 {"-Wno-unused-value"}); 1291 const auto Results = 1292 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1293 EXPECT_TRUE(isMutated(Results, AST.get())); 1294 } 1295 1296 TEST(ExprMutationAnalyzerTest, CommaExprAsUniquePtr) { 1297 const std::string UniquePtrDef = "template <class T> struct UniquePtr {" 1298 " UniquePtr();" 1299 " UniquePtr(const UniquePtr&) = delete;" 1300 " T& operator*() const;" 1301 " T* operator->() const;" 1302 "};"; 1303 const auto AST = buildASTFromCodeWithArgs( 1304 UniquePtrDef + "template <class T> void f() " 1305 "{ UniquePtr<T> x; UniquePtr<T> y;" 1306 " (y, x)->mf(); }", 1307 {"-fno-delayed-template-parsing", "-Wno-unused-value"}); 1308 const auto Results = 1309 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1310 EXPECT_TRUE(isMutated(Results, AST.get())); 1311 } 1312 1313 TEST(ExprMutationAnalyzerTest, CommaNestedConditional) { 1314 const std::string Code = "void f() { int x, y = 42;" 1315 " y, (true ? x : y) = 42; }"; 1316 const auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"}); 1317 const auto Results = 1318 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1319 EXPECT_THAT(mutatedBy(Results, AST.get()), 1320 ElementsAre("(true ? x : y) = 42")); 1321 } 1322 1323 // section: lambda captures 1324 1325 TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByValue) { 1326 const auto AST = buildASTFromCode("void f() { int x; [=]() { x; }; }"); 1327 const auto Results = 1328 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1329 EXPECT_FALSE(isMutated(Results, AST.get())); 1330 } 1331 1332 TEST(ExprMutationAnalyzerTest, LambdaExplicitCaptureByValue) { 1333 const auto AST = buildASTFromCode("void f() { int x; [x]() { x; }; }"); 1334 const auto Results = 1335 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1336 EXPECT_FALSE(isMutated(Results, AST.get())); 1337 } 1338 1339 TEST(ExprMutationAnalyzerTest, LambdaDefaultCaptureByRef) { 1340 const auto AST = buildASTFromCode("void f() { int 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=10;}"))); 1345 } 1346 1347 TEST(ExprMutationAnalyzerTest, LambdaExplicitCaptureByRef) { 1348 const auto AST = buildASTFromCode("void f() { int x; [&x]() { x = 10; }; }"); 1349 const auto Results = 1350 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1351 EXPECT_THAT(mutatedBy(Results, AST.get()), 1352 ElementsAre(ResultOf(removeSpace, "[&x](){x=10;}"))); 1353 } 1354 1355 // section: range-for loops 1356 1357 TEST(ExprMutationAnalyzerTest, RangeForArrayByRefModified) { 1358 auto AST = 1359 buildASTFromCode("void f() { int x[2]; for (int& e : x) e = 10; }"); 1360 auto Results = 1361 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1362 EXPECT_THAT(mutatedBy(Results, AST.get()), 1363 ElementsAre("for (int &e : x)\n e = 10;")); 1364 1365 AST = buildASTFromCode("typedef int& IntRef;" 1366 "void f() { int x[2]; for (IntRef e : x) e = 10; }"); 1367 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1368 EXPECT_THAT(mutatedBy(Results, AST.get()), 1369 ElementsAre("for (IntRef e : x)\n e = 10;")); 1370 } 1371 1372 TEST(ExprMutationAnalyzerTest, RangeForArrayByRefModifiedByImplicitInit) { 1373 const auto AST = 1374 buildASTFromCode("void f() { int x[2]; for (int& e : x) e; }"); 1375 const auto Results = 1376 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1377 EXPECT_TRUE(isMutated(Results, AST.get())); 1378 } 1379 1380 TEST(ExprMutationAnalyzerTest, RangeForArrayByValue) { 1381 auto AST = buildASTFromCode("void f() { int x[2]; for (int e : x) e = 10; }"); 1382 auto Results = 1383 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1384 EXPECT_FALSE(isMutated(Results, AST.get())); 1385 1386 AST = 1387 buildASTFromCode("void f() { int* x[2]; for (int* e : x) e = nullptr; }"); 1388 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1389 EXPECT_TRUE(isMutated(Results, AST.get())); 1390 1391 AST = buildASTFromCode( 1392 "typedef int* IntPtr;" 1393 "void f() { int* x[2]; for (IntPtr e : x) e = nullptr; }"); 1394 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1395 EXPECT_TRUE(isMutated(Results, AST.get())); 1396 } 1397 1398 TEST(ExprMutationAnalyzerTest, RangeForArrayByConstRef) { 1399 auto AST = 1400 buildASTFromCode("void f() { int x[2]; for (const int& e : x) e; }"); 1401 auto Results = 1402 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1403 EXPECT_FALSE(isMutated(Results, AST.get())); 1404 1405 AST = buildASTFromCode("typedef const int& CIntRef;" 1406 "void f() { int x[2]; for (CIntRef e : x) e; }"); 1407 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1408 EXPECT_FALSE(isMutated(Results, AST.get())); 1409 } 1410 1411 TEST(ExprMutationAnalyzerTest, RangeForNonArrayByRefModified) { 1412 const auto AST = 1413 buildASTFromCode("struct V { int* begin(); int* end(); };" 1414 "void f() { V x; for (int& e : x) e = 10; }"); 1415 const auto Results = 1416 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1417 EXPECT_THAT(mutatedBy(Results, AST.get()), 1418 ElementsAre("for (int &e : x)\n e = 10;")); 1419 } 1420 1421 TEST(ExprMutationAnalyzerTest, RangeForNonArrayByRefNotModified) { 1422 const auto AST = buildASTFromCode("struct V { int* begin(); int* end(); };" 1423 "void f() { V x; for (int& e : x) e; }"); 1424 const auto Results = 1425 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1426 EXPECT_TRUE(isMutated(Results, AST.get())); 1427 } 1428 1429 TEST(ExprMutationAnalyzerTest, RangeForNonArrayByValue) { 1430 const auto AST = buildASTFromCode( 1431 "struct V { const int* begin() const; const int* end() const; };" 1432 "void f() { V x; for (int e : x) e; }"); 1433 const auto Results = 1434 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1435 EXPECT_FALSE(isMutated(Results, AST.get())); 1436 } 1437 1438 TEST(ExprMutationAnalyzerTest, RangeForNonArrayByConstRef) { 1439 const auto AST = buildASTFromCode( 1440 "struct V { const int* begin() const; const int* end() const; };" 1441 "void f() { V x; for (const int& e : x) e; }"); 1442 const auto Results = 1443 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1444 EXPECT_FALSE(isMutated(Results, AST.get())); 1445 } 1446 1447 // section: unevaluated expressions 1448 1449 TEST(ExprMutationAnalyzerTest, UnevaluatedExpressions) { 1450 auto AST = buildASTFromCode("void f() { int x, y; decltype(x = 10) z = y; }"); 1451 auto Results = 1452 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1453 EXPECT_FALSE(isMutated(Results, AST.get())); 1454 1455 AST = buildASTFromCode("void f() { int x, y; __typeof(x = 10) z = y; }"); 1456 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1457 EXPECT_FALSE(isMutated(Results, AST.get())); 1458 1459 AST = buildASTFromCode("void f() { int x, y; __typeof__(x = 10) z = y; }"); 1460 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1461 EXPECT_FALSE(isMutated(Results, AST.get())); 1462 1463 AST = buildASTFromCode("void f() { int x; sizeof(x = 10); }"); 1464 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1465 EXPECT_FALSE(isMutated(Results, AST.get())); 1466 1467 AST = buildASTFromCode("void f() { int x; alignof(x = 10); }"); 1468 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1469 EXPECT_FALSE(isMutated(Results, AST.get())); 1470 1471 AST = buildASTFromCode("void f() { int x; noexcept(x = 10); }"); 1472 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1473 EXPECT_FALSE(isMutated(Results, AST.get())); 1474 1475 AST = buildASTFromCodeWithArgs("namespace std { class type_info; }" 1476 "void f() { int x; typeid(x = 10); }", 1477 {"-frtti"}); 1478 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1479 EXPECT_FALSE(isMutated(Results, AST.get())); 1480 1481 AST = buildASTFromCode( 1482 "void f() { int x; _Generic(x = 10, int: 0, default: 1); }"); 1483 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1484 EXPECT_FALSE(isMutated(Results, AST.get())); 1485 } 1486 1487 TEST(ExprMutationAnalyzerTest, NotUnevaluatedExpressions) { 1488 auto AST = buildASTFromCode("void f() { int x; sizeof(int[x++]); }"); 1489 auto Results = 1490 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1491 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x++")); 1492 1493 AST = buildASTFromCodeWithArgs( 1494 "namespace std { class type_info; }" 1495 "struct A { virtual ~A(); }; struct B : A {};" 1496 "struct X { A& f(); }; void f() { X x; typeid(x.f()); }", 1497 {"-frtti"}); 1498 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1499 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.f()")); 1500 } 1501 1502 // section: special case: smartpointers 1503 1504 TEST(ExprMutationAnalyzerTest, UniquePtr) { 1505 const std::string UniquePtrDef = 1506 "template <class T> struct UniquePtr {" 1507 " UniquePtr();" 1508 " UniquePtr(const UniquePtr&) = delete;" 1509 " UniquePtr(UniquePtr&&);" 1510 " UniquePtr& operator=(const UniquePtr&) = delete;" 1511 " UniquePtr& operator=(UniquePtr&&);" 1512 " T& operator*() const;" 1513 " T* operator->() const;" 1514 "};"; 1515 1516 auto AST = buildASTFromCode(UniquePtrDef + 1517 "void f() { UniquePtr<int> x; *x = 10; }"); 1518 auto Results = 1519 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1520 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("* x = 10")); 1521 1522 AST = buildASTFromCode(UniquePtrDef + "void f() { UniquePtr<int> x; *x; }"); 1523 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1524 EXPECT_FALSE(isMutated(Results, AST.get())); 1525 1526 AST = buildASTFromCode(UniquePtrDef + 1527 "void f() { UniquePtr<const int> x; *x; }"); 1528 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1529 EXPECT_FALSE(isMutated(Results, AST.get())); 1530 1531 AST = buildASTFromCode(UniquePtrDef + "struct S { int v; };" 1532 "void f() { UniquePtr<S> x; x->v; }"); 1533 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1534 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); 1535 1536 AST = buildASTFromCode(UniquePtrDef + 1537 "struct S { int v; };" 1538 "void f() { UniquePtr<const S> x; x->v; }"); 1539 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1540 EXPECT_FALSE(isMutated(Results, AST.get())); 1541 1542 AST = 1543 buildASTFromCode(UniquePtrDef + "struct S { void mf(); };" 1544 "void f() { UniquePtr<S> x; x->mf(); }"); 1545 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1546 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x")); 1547 1548 AST = buildASTFromCode(UniquePtrDef + 1549 "struct S { void mf() const; };" 1550 "void f() { UniquePtr<const S> x; x->mf(); }"); 1551 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1552 EXPECT_FALSE(isMutated(Results, AST.get())); 1553 1554 AST = buildASTFromCodeWithArgs( 1555 UniquePtrDef + "template <class T> void f() { UniquePtr<T> x; x->mf(); }", 1556 {"-fno-delayed-template-parsing"}); 1557 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1558 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x->mf()")); 1559 } 1560 1561 // section: complex problems detected on real code 1562 1563 TEST(ExprMutationAnalyzerTest, SelfRef) { 1564 std::unique_ptr<ASTUnit> AST{}; 1565 SmallVector<BoundNodes, 1> Results{}; 1566 1567 AST = buildASTFromCodeWithArgs("void f() { int &x = x; }", 1568 {"-Wno-unused-value", "-Wno-uninitialized"}); 1569 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1570 EXPECT_FALSE(isDeclMutated(Results, AST.get())); 1571 1572 AST = buildASTFromCodeWithArgs("void f() { int &x = x; x = 1; }", 1573 {"-Wno-unused-value", "-Wno-uninitialized"}); 1574 Results = match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1575 EXPECT_TRUE(isDeclMutated(Results, AST.get())); 1576 } 1577 1578 TEST(ExprMutationAnalyzerTest, UnevaluatedContext) { 1579 const std::string Example = 1580 "template <typename T>" 1581 "struct to_construct : T { to_construct(int &j) {} };" 1582 "template <typename T>" 1583 "void placement_new_in_unique_ptr() { int x = 0;" 1584 " new to_construct<T>(x);" 1585 "}"; 1586 auto AST = 1587 buildASTFromCodeWithArgs(Example, {"-fno-delayed-template-parsing"}); 1588 auto Results = 1589 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1590 EXPECT_TRUE(isMutated(Results, AST.get())); 1591 EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(x)")); 1592 } 1593 1594 TEST(ExprMutationAnalyzerTest, ReproduceFailureMinimal) { 1595 const std::string Reproducer = 1596 "namespace std {" 1597 "template <class T> T &forward(T &A) { return static_cast<T&&>(A); }" 1598 "template <class T> struct __bind {" 1599 " T f;" 1600 " template <class V> __bind(T v, V &&) : f(forward(v)) {}" 1601 "};" 1602 "}" 1603 "void f() {" 1604 " int x = 42;" 1605 " auto Lambda = [] {};" 1606 " std::__bind<decltype(Lambda)>(Lambda, x);" 1607 "}"; 1608 auto AST11 = buildASTFromCodeWithArgs(Reproducer, {"-std=c++11"}); 1609 auto Results11 = 1610 match(withEnclosingCompound(declRefTo("x")), AST11->getASTContext()); 1611 EXPECT_FALSE(isMutated(Results11, AST11.get())); 1612 } 1613 1614 static bool isPointeeMutated(const SmallVectorImpl<BoundNodes> &Results, 1615 ASTUnit *AST) { 1616 const auto *const S = selectFirst<Stmt>("stmt", Results); 1617 const auto *const E = selectFirst<Expr>("expr", Results); 1618 assert(S && E); 1619 TraversalKindScope RAII(AST->getASTContext(), TK_AsIs); 1620 return ExprMutationAnalyzer(*S, AST->getASTContext()).isPointeeMutated(E); 1621 } 1622 1623 static bool isDeclPointeeMutated(const SmallVectorImpl<BoundNodes> &Results, 1624 ASTUnit *AST) { 1625 const auto *const S = selectFirst<Stmt>("stmt", Results); 1626 const auto *const D = selectFirst<Decl>("decl", Results); 1627 assert(S && D); 1628 TraversalKindScope RAII(AST->getASTContext(), TK_AsIs); 1629 return ExprMutationAnalyzer(*S, AST->getASTContext()).isPointeeMutated(D); 1630 } 1631 1632 TEST(ExprMutationAnalyzerTest, PointeeMutatedByAssign) { 1633 { 1634 const std::string Code = "void f() { int* x = nullptr; int b = *x; }"; 1635 auto AST = buildASTFromCodeWithArgs(Code, {}); 1636 auto Results = 1637 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1638 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1639 } 1640 { 1641 const std::string Code = "void f() { int* x = nullptr; *x = 100; }"; 1642 auto AST = buildASTFromCodeWithArgs(Code, {}); 1643 auto Results = 1644 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1645 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1646 } 1647 { 1648 const std::string Code = "void f() { int* x = nullptr; (*x)++; }"; 1649 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"}); 1650 auto Results = 1651 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1652 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1653 } 1654 } 1655 1656 TEST(ExprMutationAnalyzerTest, PointeeMutatedByMember) { 1657 { 1658 const std::string Code = 1659 "struct A { int v; }; void f() { A* x = nullptr; int b = x->v; }"; 1660 auto AST = buildASTFromCodeWithArgs(Code, {}); 1661 auto Results = 1662 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1663 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1664 } 1665 { 1666 const std::string Code = 1667 "struct A { int v; }; void f() { A* x = nullptr; x->v = 1; }"; 1668 auto AST = buildASTFromCodeWithArgs(Code, {}); 1669 auto Results = 1670 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1671 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1672 } 1673 { 1674 const std::string Code = 1675 "struct A { int v; }; void f() { A* x = nullptr; x->v++; }"; 1676 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-unused-value"}); 1677 auto Results = 1678 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1679 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1680 } 1681 } 1682 TEST(ExprMutationAnalyzerTest, PointeeMutatedByMethod) { 1683 { 1684 const std::string Code = "struct A { int v; void foo(); };" 1685 "void f() { A* x = nullptr; x->foo(); }"; 1686 auto AST = buildASTFromCodeWithArgs(Code, {}); 1687 auto Results = 1688 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1689 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1690 } 1691 { 1692 const std::string Code = "struct A { int v; void foo() const; };" 1693 "void f() { A* x = nullptr; x->foo(); }"; 1694 auto AST = buildASTFromCodeWithArgs(Code, {}); 1695 auto Results = 1696 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1697 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1698 } 1699 } 1700 TEST(ExprMutationAnalyzerTest, PointeeMutatedByOperatorOverload) { 1701 { 1702 const std::string Code = "struct A { int v; int operator++(); };" 1703 "void f() { A* x = nullptr; x->operator++(); }"; 1704 auto AST = buildASTFromCodeWithArgs(Code, {}); 1705 auto Results = 1706 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1707 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1708 } 1709 { 1710 const std::string Code = "struct A { int v; int operator++() const; };" 1711 "void f() { A* x = nullptr; x->operator++(); }"; 1712 auto AST = buildASTFromCodeWithArgs(Code, {}); 1713 auto Results = 1714 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1715 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1716 } 1717 } 1718 1719 TEST(ExprMutationAnalyzerTest, PointeeMutatedByInitToNonConst) { 1720 { 1721 const std::string Code = "void f() { int* x = nullptr; int const* b = x; }"; 1722 auto AST = buildASTFromCodeWithArgs(Code, {}); 1723 auto Results = 1724 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1725 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1726 } 1727 { 1728 const std::string Code = "void f() { int* x = nullptr; int* b = x; }"; 1729 auto AST = buildASTFromCodeWithArgs(Code, {}); 1730 auto Results = 1731 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1732 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1733 } 1734 { 1735 const std::string Code = "void f() { int* x = nullptr; int* const b = x; }"; 1736 auto AST = buildASTFromCodeWithArgs(Code, {}); 1737 auto Results = 1738 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1739 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1740 } 1741 } 1742 1743 TEST(ExprMutationAnalyzerTest, PointeeMutatedByAssignToNonConst) { 1744 { 1745 const std::string Code = 1746 "void f() { int* x = nullptr; int const* b; b = x; }"; 1747 auto AST = buildASTFromCodeWithArgs(Code, {}); 1748 auto Results = 1749 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1750 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1751 } 1752 { 1753 const std::string Code = "void f() { int* x = nullptr; int* b; b = x; }"; 1754 auto AST = buildASTFromCodeWithArgs(Code, {}); 1755 auto Results = 1756 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1757 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1758 } 1759 } 1760 1761 TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgument) { 1762 { 1763 const std::string Code = 1764 "void b(int const*); void f() { int* x = nullptr; b(x); }"; 1765 auto AST = buildASTFromCodeWithArgs(Code, {}); 1766 auto Results = 1767 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1768 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1769 } 1770 { 1771 const std::string Code = 1772 "void b(int *); void f() { int* x = nullptr; b(x); }"; 1773 auto AST = buildASTFromCodeWithArgs(Code, {}); 1774 auto Results = 1775 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1776 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1777 } 1778 } 1779 1780 TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgumentInConstruct) { 1781 { 1782 const std::string Code = "struct A { A(int const*); };" 1783 "void f() { int *x; A a{x}; }"; 1784 auto AST = buildASTFromCodeWithArgs(Code, {}); 1785 auto Results = 1786 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1787 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1788 } 1789 { 1790 const std::string Code = "struct A { A(int const*); };" 1791 "void f() { int *x; A a(x); }"; 1792 auto AST = buildASTFromCodeWithArgs(Code, {}); 1793 auto Results = 1794 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1795 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1796 } 1797 { 1798 const std::string Code = "struct A { A(int const*); };" 1799 "void f() { int *x; A a = x; }"; 1800 auto AST = buildASTFromCodeWithArgs(Code, {}); 1801 auto Results = 1802 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1803 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1804 } 1805 { 1806 const std::string Code = "struct A { A(int *); };" 1807 "void f() { int *x; A a{x}; }"; 1808 auto AST = buildASTFromCodeWithArgs(Code, {}); 1809 auto Results = 1810 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1811 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1812 } 1813 } 1814 1815 TEST(ExprMutationAnalyzerTest, 1816 PointeeMutatedByPassAsArgumentInTemplateConstruct) { 1817 const std::string Code = "template<class T> void f() { int *x; new T(x); }"; 1818 auto AST = buildASTFromCodeWithArgs(Code, {"-fno-delayed-template-parsing"}); 1819 auto Results = 1820 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1821 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1822 } 1823 1824 TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgumentInInitList) { 1825 { 1826 const std::string Code = 1827 "namespace std {" 1828 "template<class T>" 1829 "struct initializer_list{ T const* begin; T const* end; };" 1830 "}" 1831 "void f() { int *x; std::initializer_list<int*> a{x, x, x}; }"; 1832 auto AST = 1833 buildASTFromCodeWithArgs(Code, {"-fno-delayed-template-parsing"}); 1834 auto Results = 1835 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1836 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1837 } 1838 } 1839 1840 TEST(ExprMutationAnalyzerTest, PointeeMutatedByThis) { 1841 { 1842 const std::string Code = 1843 "struct A { void m() const; }; void f() { A* x = nullptr; x->m(); }"; 1844 auto AST = buildASTFromCodeWithArgs(Code, {}); 1845 auto Results = 1846 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1847 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1848 } 1849 { 1850 const std::string Code = 1851 "struct A { void m(); }; void f() { A* x = nullptr; x->m(); }"; 1852 auto AST = buildASTFromCodeWithArgs(Code, {}); 1853 auto Results = 1854 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1855 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1856 } 1857 } 1858 1859 TEST(ExprMutationAnalyzerTest, PointeeMutatedByExplicitCastToNonConst) { 1860 { 1861 const std::string Code = 1862 "void f() { int* x = nullptr; static_cast<int const*>(x); }"; 1863 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"}); 1864 auto Results = 1865 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1866 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1867 } 1868 { 1869 const std::string Code = 1870 "void f() { int* x = nullptr; static_cast<int*>(x); }"; 1871 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"}); 1872 auto Results = 1873 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1874 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1875 } 1876 } 1877 1878 TEST(ExprMutationAnalyzerTest, PointeeMutatedByConstCastToNonConst) { 1879 // const_cast to non-const even treat as mutated. 1880 { 1881 const std::string Code = 1882 "void f() { int* x = nullptr; const_cast<int const*>(x); }"; 1883 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"}); 1884 auto Results = 1885 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1886 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1887 } 1888 { 1889 const std::string Code = 1890 "void f() { int* x = nullptr; const_cast<int*>(x); }"; 1891 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"}); 1892 auto Results = 1893 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1894 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1895 } 1896 } 1897 1898 TEST(ExprMutationAnalyzerTest, PointeeMutatedByUnresolvedCall) { 1899 const std::string Code = 1900 "template <class T> struct S;" 1901 "template <class T> void f() { S<T> s; int* x = nullptr; s.m(x); }"; 1902 auto AST = buildASTFromCodeWithArgs( 1903 Code, {"-fno-delayed-template-parsing", "-Wno-everything"}); 1904 auto Results = 1905 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1906 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1907 } 1908 1909 TEST(ExprMutationAnalyzerTest, PointeeMutatedByAssignToUnknownType) { 1910 { 1911 const std::string Code = "template <class T> void f() {" 1912 " int* x = nullptr;" 1913 " T t = x;" 1914 "}"; 1915 auto AST = buildASTFromCodeWithArgs( 1916 Code, {"-fno-delayed-template-parsing", "-Wno-everything"}); 1917 auto Results = 1918 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1919 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1920 } 1921 { 1922 const std::string Code = "template <class T> void f() {" 1923 " int* x = nullptr;" 1924 " typename T::t t = x;" 1925 "}"; 1926 auto AST = buildASTFromCodeWithArgs( 1927 Code, {"-fno-delayed-template-parsing", "-Wno-everything"}); 1928 auto Results = 1929 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1930 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1931 } 1932 } 1933 1934 TEST(ExprMutationAnalyzerTest, PointeeMutatedByLambdaCapture) { 1935 const std::string Code = R"( 1936 void f() { 1937 int* x; 1938 [x] () { *x = 1; }; 1939 })"; 1940 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"}); 1941 auto Results = 1942 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1943 EXPECT_TRUE(isDeclPointeeMutated(Results, AST.get())); 1944 } 1945 1946 TEST(ExprMutationAnalyzerTest, PointeeMutatedByLambdaCaptureInit) { 1947 const std::string Code = R"( 1948 void f() { 1949 int* x; 1950 [t = x] () { *t = 1; }; 1951 })"; 1952 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"}); 1953 auto Results = 1954 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1955 EXPECT_TRUE(isDeclPointeeMutated(Results, AST.get())); 1956 } 1957 1958 TEST(ExprMutationAnalyzerTest, PointeeMutatedByPointerArithmeticAdd) { 1959 { 1960 const std::string Code = R"( 1961 void f() { 1962 int* x; 1963 int* y = x + 1; 1964 })"; 1965 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"}); 1966 auto Results = 1967 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1968 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1969 } 1970 { 1971 const std::string Code = R"( 1972 void f() { 1973 int* x; 1974 x + 1; 1975 })"; 1976 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"}); 1977 auto Results = 1978 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1979 EXPECT_FALSE(isPointeeMutated(Results, AST.get())); 1980 } 1981 } 1982 1983 TEST(ExprMutationAnalyzerTest, PointeeMutatedByPointerArithmeticSubElement) { 1984 const std::string Code = R"( 1985 void f() { 1986 int* x; 1987 int* y = &x[1]; 1988 })"; 1989 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"}); 1990 auto Results = 1991 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 1992 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 1993 } 1994 1995 TEST(ExprMutationAnalyzerTest, PointeeMutatedByConditionOperator) { 1996 const std::string Code = R"( 1997 void f() { 1998 int* x; 1999 int* y = 1 ? nullptr : x; 2000 })"; 2001 auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"}); 2002 auto Results = 2003 match(withEnclosingCompound(declRefTo("x")), AST->getASTContext()); 2004 EXPECT_TRUE(isPointeeMutated(Results, AST.get())); 2005 } 2006 2007 } // namespace clang 2008