brintos

brintos / llvm-project-archived public Read only

0
0
Text · 85.9 KiB · 8fc9a66 Raw
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