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