xref: /freebsd/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/Transfer.cpp (revision 972a253a57b6f144b0e4a3e2080a2a0076ec55a0)
104eeddc0SDimitry Andric //===-- Transfer.cpp --------------------------------------------*- C++ -*-===//
204eeddc0SDimitry Andric //
304eeddc0SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
404eeddc0SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
504eeddc0SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
604eeddc0SDimitry Andric //
704eeddc0SDimitry Andric //===----------------------------------------------------------------------===//
804eeddc0SDimitry Andric //
904eeddc0SDimitry Andric //  This file defines transfer functions that evaluate program statements and
1004eeddc0SDimitry Andric //  update an environment accordingly.
1104eeddc0SDimitry Andric //
1204eeddc0SDimitry Andric //===----------------------------------------------------------------------===//
1304eeddc0SDimitry Andric 
1404eeddc0SDimitry Andric #include "clang/Analysis/FlowSensitive/Transfer.h"
1504eeddc0SDimitry Andric #include "clang/AST/Decl.h"
1604eeddc0SDimitry Andric #include "clang/AST/DeclBase.h"
1704eeddc0SDimitry Andric #include "clang/AST/DeclCXX.h"
1804eeddc0SDimitry Andric #include "clang/AST/Expr.h"
1904eeddc0SDimitry Andric #include "clang/AST/ExprCXX.h"
2004eeddc0SDimitry Andric #include "clang/AST/OperationKinds.h"
2104eeddc0SDimitry Andric #include "clang/AST/Stmt.h"
2204eeddc0SDimitry Andric #include "clang/AST/StmtVisitor.h"
23*972a253aSDimitry Andric #include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
2404eeddc0SDimitry Andric #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
25*972a253aSDimitry Andric #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
2681ad6265SDimitry Andric #include "clang/Analysis/FlowSensitive/Value.h"
2781ad6265SDimitry Andric #include "clang/Basic/Builtins.h"
2804eeddc0SDimitry Andric #include "clang/Basic/OperatorKinds.h"
2904eeddc0SDimitry Andric #include "llvm/ADT/STLExtras.h"
3004eeddc0SDimitry Andric #include "llvm/Support/Casting.h"
3104eeddc0SDimitry Andric #include <cassert>
3204eeddc0SDimitry Andric #include <memory>
3304eeddc0SDimitry Andric #include <tuple>
3404eeddc0SDimitry Andric 
3504eeddc0SDimitry Andric namespace clang {
3604eeddc0SDimitry Andric namespace dataflow {
3704eeddc0SDimitry Andric 
3881ad6265SDimitry Andric static BoolValue &evaluateBooleanEquality(const Expr &LHS, const Expr &RHS,
3981ad6265SDimitry Andric                                           Environment &Env) {
4081ad6265SDimitry Andric   if (auto *LHSValue =
4181ad6265SDimitry Andric           dyn_cast_or_null<BoolValue>(Env.getValue(LHS, SkipPast::Reference)))
4281ad6265SDimitry Andric     if (auto *RHSValue =
4381ad6265SDimitry Andric             dyn_cast_or_null<BoolValue>(Env.getValue(RHS, SkipPast::Reference)))
4481ad6265SDimitry Andric       return Env.makeIff(*LHSValue, *RHSValue);
4581ad6265SDimitry Andric 
4681ad6265SDimitry Andric   return Env.makeAtomicBoolValue();
4704eeddc0SDimitry Andric }
4804eeddc0SDimitry Andric 
4904eeddc0SDimitry Andric class TransferVisitor : public ConstStmtVisitor<TransferVisitor> {
5004eeddc0SDimitry Andric public:
51*972a253aSDimitry Andric   TransferVisitor(const StmtToEnvMap &StmtToEnv, Environment &Env,
52*972a253aSDimitry Andric                   TransferOptions Options)
53*972a253aSDimitry Andric       : StmtToEnv(StmtToEnv), Env(Env), Options(Options) {}
5404eeddc0SDimitry Andric 
5504eeddc0SDimitry Andric   void VisitBinaryOperator(const BinaryOperator *S) {
5681ad6265SDimitry Andric     const Expr *LHS = S->getLHS();
5704eeddc0SDimitry Andric     assert(LHS != nullptr);
5881ad6265SDimitry Andric 
5981ad6265SDimitry Andric     const Expr *RHS = S->getRHS();
6081ad6265SDimitry Andric     assert(RHS != nullptr);
6181ad6265SDimitry Andric 
6281ad6265SDimitry Andric     switch (S->getOpcode()) {
6381ad6265SDimitry Andric     case BO_Assign: {
6404eeddc0SDimitry Andric       auto *LHSLoc = Env.getStorageLocation(*LHS, SkipPast::Reference);
6504eeddc0SDimitry Andric       if (LHSLoc == nullptr)
6681ad6265SDimitry Andric         break;
6704eeddc0SDimitry Andric 
6881ad6265SDimitry Andric       auto *RHSVal = Env.getValue(*RHS, SkipPast::Reference);
6904eeddc0SDimitry Andric       if (RHSVal == nullptr)
7081ad6265SDimitry Andric         break;
7104eeddc0SDimitry Andric 
7204eeddc0SDimitry Andric       // Assign a value to the storage location of the left-hand side.
7304eeddc0SDimitry Andric       Env.setValue(*LHSLoc, *RHSVal);
7404eeddc0SDimitry Andric 
7504eeddc0SDimitry Andric       // Assign a storage location for the whole expression.
7604eeddc0SDimitry Andric       Env.setStorageLocation(*S, *LHSLoc);
7781ad6265SDimitry Andric       break;
7804eeddc0SDimitry Andric     }
7981ad6265SDimitry Andric     case BO_LAnd:
8081ad6265SDimitry Andric     case BO_LOr: {
8181ad6265SDimitry Andric       BoolValue &LHSVal = getLogicOperatorSubExprValue(*LHS);
8281ad6265SDimitry Andric       BoolValue &RHSVal = getLogicOperatorSubExprValue(*RHS);
8381ad6265SDimitry Andric 
8481ad6265SDimitry Andric       auto &Loc = Env.createStorageLocation(*S);
8581ad6265SDimitry Andric       Env.setStorageLocation(*S, Loc);
8681ad6265SDimitry Andric       if (S->getOpcode() == BO_LAnd)
8781ad6265SDimitry Andric         Env.setValue(Loc, Env.makeAnd(LHSVal, RHSVal));
8881ad6265SDimitry Andric       else
8981ad6265SDimitry Andric         Env.setValue(Loc, Env.makeOr(LHSVal, RHSVal));
9081ad6265SDimitry Andric       break;
9181ad6265SDimitry Andric     }
9281ad6265SDimitry Andric     case BO_NE:
9381ad6265SDimitry Andric     case BO_EQ: {
9481ad6265SDimitry Andric       auto &LHSEqRHSValue = evaluateBooleanEquality(*LHS, *RHS, Env);
9581ad6265SDimitry Andric       auto &Loc = Env.createStorageLocation(*S);
9681ad6265SDimitry Andric       Env.setStorageLocation(*S, Loc);
9781ad6265SDimitry Andric       Env.setValue(Loc, S->getOpcode() == BO_EQ ? LHSEqRHSValue
9881ad6265SDimitry Andric                                                 : Env.makeNot(LHSEqRHSValue));
9981ad6265SDimitry Andric       break;
10081ad6265SDimitry Andric     }
10181ad6265SDimitry Andric     case BO_Comma: {
10281ad6265SDimitry Andric       if (auto *Loc = Env.getStorageLocation(*RHS, SkipPast::None))
10381ad6265SDimitry Andric         Env.setStorageLocation(*S, *Loc);
10481ad6265SDimitry Andric       break;
10581ad6265SDimitry Andric     }
10681ad6265SDimitry Andric     default:
10781ad6265SDimitry Andric       break;
10881ad6265SDimitry Andric     }
10904eeddc0SDimitry Andric   }
11004eeddc0SDimitry Andric 
11104eeddc0SDimitry Andric   void VisitDeclRefExpr(const DeclRefExpr *S) {
11204eeddc0SDimitry Andric     assert(S->getDecl() != nullptr);
11304eeddc0SDimitry Andric     auto *DeclLoc = Env.getStorageLocation(*S->getDecl(), SkipPast::None);
11404eeddc0SDimitry Andric     if (DeclLoc == nullptr)
11504eeddc0SDimitry Andric       return;
11604eeddc0SDimitry Andric 
11704eeddc0SDimitry Andric     if (S->getDecl()->getType()->isReferenceType()) {
11804eeddc0SDimitry Andric       Env.setStorageLocation(*S, *DeclLoc);
11904eeddc0SDimitry Andric     } else {
12004eeddc0SDimitry Andric       auto &Loc = Env.createStorageLocation(*S);
12104eeddc0SDimitry Andric       auto &Val = Env.takeOwnership(std::make_unique<ReferenceValue>(*DeclLoc));
12204eeddc0SDimitry Andric       Env.setStorageLocation(*S, Loc);
12304eeddc0SDimitry Andric       Env.setValue(Loc, Val);
12404eeddc0SDimitry Andric     }
12504eeddc0SDimitry Andric   }
12604eeddc0SDimitry Andric 
12704eeddc0SDimitry Andric   void VisitDeclStmt(const DeclStmt *S) {
12804eeddc0SDimitry Andric     // Group decls are converted into single decls in the CFG so the cast below
12904eeddc0SDimitry Andric     // is safe.
13004eeddc0SDimitry Andric     const auto &D = *cast<VarDecl>(S->getSingleDecl());
13181ad6265SDimitry Andric 
13281ad6265SDimitry Andric     // Static local vars are already initialized in `Environment`.
13381ad6265SDimitry Andric     if (D.hasGlobalStorage())
13481ad6265SDimitry Andric       return;
13581ad6265SDimitry Andric 
13604eeddc0SDimitry Andric     auto &Loc = Env.createStorageLocation(D);
13704eeddc0SDimitry Andric     Env.setStorageLocation(D, Loc);
13804eeddc0SDimitry Andric 
13904eeddc0SDimitry Andric     const Expr *InitExpr = D.getInit();
14004eeddc0SDimitry Andric     if (InitExpr == nullptr) {
14104eeddc0SDimitry Andric       // No initializer expression - associate `Loc` with a new value.
14204eeddc0SDimitry Andric       if (Value *Val = Env.createValue(D.getType()))
14304eeddc0SDimitry Andric         Env.setValue(Loc, *Val);
14404eeddc0SDimitry Andric       return;
14504eeddc0SDimitry Andric     }
14604eeddc0SDimitry Andric 
14704eeddc0SDimitry Andric     if (D.getType()->isReferenceType()) {
14804eeddc0SDimitry Andric       // Initializing a reference variable - do not create a reference to
14904eeddc0SDimitry Andric       // reference.
15004eeddc0SDimitry Andric       if (auto *InitExprLoc =
15104eeddc0SDimitry Andric               Env.getStorageLocation(*InitExpr, SkipPast::Reference)) {
15204eeddc0SDimitry Andric         auto &Val =
15304eeddc0SDimitry Andric             Env.takeOwnership(std::make_unique<ReferenceValue>(*InitExprLoc));
15404eeddc0SDimitry Andric         Env.setValue(Loc, Val);
15504eeddc0SDimitry Andric       }
15681ad6265SDimitry Andric     } else if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) {
15781ad6265SDimitry Andric       Env.setValue(Loc, *InitExprVal);
15804eeddc0SDimitry Andric     }
15904eeddc0SDimitry Andric 
16081ad6265SDimitry Andric     if (Env.getValue(Loc) == nullptr) {
16181ad6265SDimitry Andric       // We arrive here in (the few) cases where an expression is intentionally
16281ad6265SDimitry Andric       // "uninterpreted". There are two ways to handle this situation: propagate
16381ad6265SDimitry Andric       // the status, so that uninterpreted initializers result in uninterpreted
16481ad6265SDimitry Andric       // variables, or provide a default value. We choose the latter so that
16581ad6265SDimitry Andric       // later refinements of the variable can be used for reasoning about the
16681ad6265SDimitry Andric       // surrounding code.
16781ad6265SDimitry Andric       //
16881ad6265SDimitry Andric       // FIXME. If and when we interpret all language cases, change this to
16981ad6265SDimitry Andric       // assert that `InitExpr` is interpreted, rather than supplying a default
17081ad6265SDimitry Andric       // value (assuming we don't update the environment API to return
17181ad6265SDimitry Andric       // references).
17204eeddc0SDimitry Andric       if (Value *Val = Env.createValue(D.getType()))
17304eeddc0SDimitry Andric         Env.setValue(Loc, *Val);
17481ad6265SDimitry Andric     }
17581ad6265SDimitry Andric 
17681ad6265SDimitry Andric     if (const auto *Decomp = dyn_cast<DecompositionDecl>(&D)) {
17781ad6265SDimitry Andric       // If VarDecl is a DecompositionDecl, evaluate each of its bindings. This
17881ad6265SDimitry Andric       // needs to be evaluated after initializing the values in the storage for
17981ad6265SDimitry Andric       // VarDecl, as the bindings refer to them.
18081ad6265SDimitry Andric       // FIXME: Add support for ArraySubscriptExpr.
18181ad6265SDimitry Andric       // FIXME: Consider adding AST nodes that are used for structured bindings
18281ad6265SDimitry Andric       // to the CFG.
18381ad6265SDimitry Andric       for (const auto *B : Decomp->bindings()) {
18481ad6265SDimitry Andric         auto *ME = dyn_cast_or_null<MemberExpr>(B->getBinding());
18581ad6265SDimitry Andric         if (ME == nullptr)
18681ad6265SDimitry Andric           continue;
18781ad6265SDimitry Andric 
18881ad6265SDimitry Andric         auto *DE = dyn_cast_or_null<DeclRefExpr>(ME->getBase());
18981ad6265SDimitry Andric         if (DE == nullptr)
19081ad6265SDimitry Andric           continue;
19181ad6265SDimitry Andric 
19281ad6265SDimitry Andric         // ME and its base haven't been visited because they aren't included in
19381ad6265SDimitry Andric         // the statements of the CFG basic block.
19481ad6265SDimitry Andric         VisitDeclRefExpr(DE);
19581ad6265SDimitry Andric         VisitMemberExpr(ME);
19681ad6265SDimitry Andric 
19781ad6265SDimitry Andric         if (auto *Loc = Env.getStorageLocation(*ME, SkipPast::Reference))
19881ad6265SDimitry Andric           Env.setStorageLocation(*B, *Loc);
19981ad6265SDimitry Andric       }
20004eeddc0SDimitry Andric     }
20104eeddc0SDimitry Andric   }
20204eeddc0SDimitry Andric 
20304eeddc0SDimitry Andric   void VisitImplicitCastExpr(const ImplicitCastExpr *S) {
20481ad6265SDimitry Andric     const Expr *SubExpr = S->getSubExpr();
20504eeddc0SDimitry Andric     assert(SubExpr != nullptr);
20604eeddc0SDimitry Andric 
20704eeddc0SDimitry Andric     switch (S->getCastKind()) {
20881ad6265SDimitry Andric     case CK_IntegralToBoolean: {
20981ad6265SDimitry Andric       // This cast creates a new, boolean value from the integral value. We
21081ad6265SDimitry Andric       // model that with a fresh value in the environment, unless it's already a
21181ad6265SDimitry Andric       // boolean.
21281ad6265SDimitry Andric       auto &Loc = Env.createStorageLocation(*S);
21381ad6265SDimitry Andric       Env.setStorageLocation(*S, Loc);
21481ad6265SDimitry Andric       if (auto *SubExprVal = dyn_cast_or_null<BoolValue>(
21581ad6265SDimitry Andric               Env.getValue(*SubExpr, SkipPast::Reference)))
21681ad6265SDimitry Andric         Env.setValue(Loc, *SubExprVal);
21781ad6265SDimitry Andric       else
21881ad6265SDimitry Andric         // FIXME: If integer modeling is added, then update this code to create
21981ad6265SDimitry Andric         // the boolean based on the integer model.
22081ad6265SDimitry Andric         Env.setValue(Loc, Env.makeAtomicBoolValue());
22181ad6265SDimitry Andric       break;
22281ad6265SDimitry Andric     }
22381ad6265SDimitry Andric 
22404eeddc0SDimitry Andric     case CK_LValueToRValue: {
22504eeddc0SDimitry Andric       auto *SubExprVal = Env.getValue(*SubExpr, SkipPast::Reference);
22604eeddc0SDimitry Andric       if (SubExprVal == nullptr)
22704eeddc0SDimitry Andric         break;
22804eeddc0SDimitry Andric 
22904eeddc0SDimitry Andric       auto &ExprLoc = Env.createStorageLocation(*S);
23004eeddc0SDimitry Andric       Env.setStorageLocation(*S, ExprLoc);
23104eeddc0SDimitry Andric       Env.setValue(ExprLoc, *SubExprVal);
23204eeddc0SDimitry Andric       break;
23304eeddc0SDimitry Andric     }
23481ad6265SDimitry Andric 
23581ad6265SDimitry Andric     case CK_IntegralCast:
23681ad6265SDimitry Andric       // FIXME: This cast creates a new integral value from the
23781ad6265SDimitry Andric       // subexpression. But, because we don't model integers, we don't
23881ad6265SDimitry Andric       // distinguish between this new value and the underlying one. If integer
23981ad6265SDimitry Andric       // modeling is added, then update this code to create a fresh location and
24081ad6265SDimitry Andric       // value.
24181ad6265SDimitry Andric     case CK_UncheckedDerivedToBase:
24281ad6265SDimitry Andric     case CK_ConstructorConversion:
24381ad6265SDimitry Andric     case CK_UserDefinedConversion:
24481ad6265SDimitry Andric       // FIXME: Add tests that excercise CK_UncheckedDerivedToBase,
24581ad6265SDimitry Andric       // CK_ConstructorConversion, and CK_UserDefinedConversion.
24604eeddc0SDimitry Andric     case CK_NoOp: {
24704eeddc0SDimitry Andric       // FIXME: Consider making `Environment::getStorageLocation` skip noop
24804eeddc0SDimitry Andric       // expressions (this and other similar expressions in the file) instead of
24904eeddc0SDimitry Andric       // assigning them storage locations.
25004eeddc0SDimitry Andric       auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
25104eeddc0SDimitry Andric       if (SubExprLoc == nullptr)
25204eeddc0SDimitry Andric         break;
25304eeddc0SDimitry Andric 
25404eeddc0SDimitry Andric       Env.setStorageLocation(*S, *SubExprLoc);
25504eeddc0SDimitry Andric       break;
25604eeddc0SDimitry Andric     }
25781ad6265SDimitry Andric     case CK_NullToPointer:
25881ad6265SDimitry Andric     case CK_NullToMemberPointer: {
25981ad6265SDimitry Andric       auto &Loc = Env.createStorageLocation(S->getType());
26081ad6265SDimitry Andric       Env.setStorageLocation(*S, Loc);
26181ad6265SDimitry Andric 
26281ad6265SDimitry Andric       auto &NullPointerVal =
26381ad6265SDimitry Andric           Env.getOrCreateNullPointerValue(S->getType()->getPointeeType());
26481ad6265SDimitry Andric       Env.setValue(Loc, NullPointerVal);
26581ad6265SDimitry Andric       break;
26681ad6265SDimitry Andric     }
26704eeddc0SDimitry Andric     default:
26804eeddc0SDimitry Andric       break;
26904eeddc0SDimitry Andric     }
27004eeddc0SDimitry Andric   }
27104eeddc0SDimitry Andric 
27204eeddc0SDimitry Andric   void VisitUnaryOperator(const UnaryOperator *S) {
27381ad6265SDimitry Andric     const Expr *SubExpr = S->getSubExpr();
27404eeddc0SDimitry Andric     assert(SubExpr != nullptr);
27504eeddc0SDimitry Andric 
27604eeddc0SDimitry Andric     switch (S->getOpcode()) {
27704eeddc0SDimitry Andric     case UO_Deref: {
27804eeddc0SDimitry Andric       // Skip past a reference to handle dereference of a dependent pointer.
27904eeddc0SDimitry Andric       const auto *SubExprVal = cast_or_null<PointerValue>(
28004eeddc0SDimitry Andric           Env.getValue(*SubExpr, SkipPast::Reference));
28104eeddc0SDimitry Andric       if (SubExprVal == nullptr)
28204eeddc0SDimitry Andric         break;
28304eeddc0SDimitry Andric 
28404eeddc0SDimitry Andric       auto &Loc = Env.createStorageLocation(*S);
28504eeddc0SDimitry Andric       Env.setStorageLocation(*S, Loc);
28604eeddc0SDimitry Andric       Env.setValue(Loc, Env.takeOwnership(std::make_unique<ReferenceValue>(
28704eeddc0SDimitry Andric                             SubExprVal->getPointeeLoc())));
28804eeddc0SDimitry Andric       break;
28904eeddc0SDimitry Andric     }
29004eeddc0SDimitry Andric     case UO_AddrOf: {
29104eeddc0SDimitry Andric       // Do not form a pointer to a reference. If `SubExpr` is assigned a
29204eeddc0SDimitry Andric       // `ReferenceValue` then form a value that points to the location of its
29304eeddc0SDimitry Andric       // pointee.
29404eeddc0SDimitry Andric       StorageLocation *PointeeLoc =
29504eeddc0SDimitry Andric           Env.getStorageLocation(*SubExpr, SkipPast::Reference);
29604eeddc0SDimitry Andric       if (PointeeLoc == nullptr)
29704eeddc0SDimitry Andric         break;
29804eeddc0SDimitry Andric 
29904eeddc0SDimitry Andric       auto &PointerLoc = Env.createStorageLocation(*S);
30004eeddc0SDimitry Andric       auto &PointerVal =
30104eeddc0SDimitry Andric           Env.takeOwnership(std::make_unique<PointerValue>(*PointeeLoc));
30204eeddc0SDimitry Andric       Env.setStorageLocation(*S, PointerLoc);
30304eeddc0SDimitry Andric       Env.setValue(PointerLoc, PointerVal);
30404eeddc0SDimitry Andric       break;
30504eeddc0SDimitry Andric     }
30681ad6265SDimitry Andric     case UO_LNot: {
30781ad6265SDimitry Andric       auto *SubExprVal =
30881ad6265SDimitry Andric           dyn_cast_or_null<BoolValue>(Env.getValue(*SubExpr, SkipPast::None));
30981ad6265SDimitry Andric       if (SubExprVal == nullptr)
31081ad6265SDimitry Andric         break;
31181ad6265SDimitry Andric 
31281ad6265SDimitry Andric       auto &ExprLoc = Env.createStorageLocation(*S);
31381ad6265SDimitry Andric       Env.setStorageLocation(*S, ExprLoc);
31481ad6265SDimitry Andric       Env.setValue(ExprLoc, Env.makeNot(*SubExprVal));
31581ad6265SDimitry Andric       break;
31681ad6265SDimitry Andric     }
31704eeddc0SDimitry Andric     default:
31804eeddc0SDimitry Andric       break;
31904eeddc0SDimitry Andric     }
32004eeddc0SDimitry Andric   }
32104eeddc0SDimitry Andric 
32204eeddc0SDimitry Andric   void VisitCXXThisExpr(const CXXThisExpr *S) {
32304eeddc0SDimitry Andric     auto *ThisPointeeLoc = Env.getThisPointeeStorageLocation();
32481ad6265SDimitry Andric     if (ThisPointeeLoc == nullptr)
32581ad6265SDimitry Andric       // Unions are not supported yet, and will not have a location for the
32681ad6265SDimitry Andric       // `this` expression's pointee.
32781ad6265SDimitry Andric       return;
32804eeddc0SDimitry Andric 
32904eeddc0SDimitry Andric     auto &Loc = Env.createStorageLocation(*S);
33004eeddc0SDimitry Andric     Env.setStorageLocation(*S, Loc);
33104eeddc0SDimitry Andric     Env.setValue(Loc, Env.takeOwnership(
33204eeddc0SDimitry Andric                           std::make_unique<PointerValue>(*ThisPointeeLoc)));
33304eeddc0SDimitry Andric   }
33404eeddc0SDimitry Andric 
33504eeddc0SDimitry Andric   void VisitMemberExpr(const MemberExpr *S) {
33604eeddc0SDimitry Andric     ValueDecl *Member = S->getMemberDecl();
33704eeddc0SDimitry Andric     assert(Member != nullptr);
33804eeddc0SDimitry Andric 
33904eeddc0SDimitry Andric     // FIXME: Consider assigning pointer values to function member expressions.
34004eeddc0SDimitry Andric     if (Member->isFunctionOrFunctionTemplate())
34104eeddc0SDimitry Andric       return;
34204eeddc0SDimitry Andric 
34381ad6265SDimitry Andric     if (auto *D = dyn_cast<VarDecl>(Member)) {
34481ad6265SDimitry Andric       if (D->hasGlobalStorage()) {
34581ad6265SDimitry Andric         auto *VarDeclLoc = Env.getStorageLocation(*D, SkipPast::None);
34681ad6265SDimitry Andric         if (VarDeclLoc == nullptr)
34781ad6265SDimitry Andric           return;
34881ad6265SDimitry Andric 
34981ad6265SDimitry Andric         if (VarDeclLoc->getType()->isReferenceType()) {
35081ad6265SDimitry Andric           Env.setStorageLocation(*S, *VarDeclLoc);
35181ad6265SDimitry Andric         } else {
35281ad6265SDimitry Andric           auto &Loc = Env.createStorageLocation(*S);
35381ad6265SDimitry Andric           Env.setStorageLocation(*S, Loc);
35481ad6265SDimitry Andric           Env.setValue(Loc, Env.takeOwnership(
35581ad6265SDimitry Andric                                 std::make_unique<ReferenceValue>(*VarDeclLoc)));
35681ad6265SDimitry Andric         }
35781ad6265SDimitry Andric         return;
35881ad6265SDimitry Andric       }
35981ad6265SDimitry Andric     }
36081ad6265SDimitry Andric 
36104eeddc0SDimitry Andric     // The receiver can be either a value or a pointer to a value. Skip past the
36204eeddc0SDimitry Andric     // indirection to handle both cases.
36304eeddc0SDimitry Andric     auto *BaseLoc = cast_or_null<AggregateStorageLocation>(
36404eeddc0SDimitry Andric         Env.getStorageLocation(*S->getBase(), SkipPast::ReferenceThenPointer));
36504eeddc0SDimitry Andric     if (BaseLoc == nullptr)
36604eeddc0SDimitry Andric       return;
36704eeddc0SDimitry Andric 
36804eeddc0SDimitry Andric     // FIXME: Add support for union types.
36904eeddc0SDimitry Andric     if (BaseLoc->getType()->isUnionType())
37004eeddc0SDimitry Andric       return;
37104eeddc0SDimitry Andric 
37204eeddc0SDimitry Andric     auto &MemberLoc = BaseLoc->getChild(*Member);
37304eeddc0SDimitry Andric     if (MemberLoc.getType()->isReferenceType()) {
37404eeddc0SDimitry Andric       Env.setStorageLocation(*S, MemberLoc);
37504eeddc0SDimitry Andric     } else {
37604eeddc0SDimitry Andric       auto &Loc = Env.createStorageLocation(*S);
37704eeddc0SDimitry Andric       Env.setStorageLocation(*S, Loc);
37804eeddc0SDimitry Andric       Env.setValue(
37904eeddc0SDimitry Andric           Loc, Env.takeOwnership(std::make_unique<ReferenceValue>(MemberLoc)));
38004eeddc0SDimitry Andric     }
38104eeddc0SDimitry Andric   }
38204eeddc0SDimitry Andric 
38304eeddc0SDimitry Andric   void VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *S) {
38404eeddc0SDimitry Andric     const Expr *InitExpr = S->getExpr();
38504eeddc0SDimitry Andric     assert(InitExpr != nullptr);
38604eeddc0SDimitry Andric 
38704eeddc0SDimitry Andric     Value *InitExprVal = Env.getValue(*InitExpr, SkipPast::None);
38804eeddc0SDimitry Andric     if (InitExprVal == nullptr)
38904eeddc0SDimitry Andric       return;
39004eeddc0SDimitry Andric 
39104eeddc0SDimitry Andric     const FieldDecl *Field = S->getField();
39204eeddc0SDimitry Andric     assert(Field != nullptr);
39304eeddc0SDimitry Andric 
39404eeddc0SDimitry Andric     auto &ThisLoc =
39504eeddc0SDimitry Andric         *cast<AggregateStorageLocation>(Env.getThisPointeeStorageLocation());
39604eeddc0SDimitry Andric     auto &FieldLoc = ThisLoc.getChild(*Field);
39704eeddc0SDimitry Andric     Env.setValue(FieldLoc, *InitExprVal);
39804eeddc0SDimitry Andric   }
39904eeddc0SDimitry Andric 
40004eeddc0SDimitry Andric   void VisitCXXConstructExpr(const CXXConstructExpr *S) {
40104eeddc0SDimitry Andric     const CXXConstructorDecl *ConstructorDecl = S->getConstructor();
40204eeddc0SDimitry Andric     assert(ConstructorDecl != nullptr);
40304eeddc0SDimitry Andric 
40404eeddc0SDimitry Andric     if (ConstructorDecl->isCopyOrMoveConstructor()) {
40504eeddc0SDimitry Andric       assert(S->getNumArgs() == 1);
40604eeddc0SDimitry Andric 
40704eeddc0SDimitry Andric       const Expr *Arg = S->getArg(0);
40804eeddc0SDimitry Andric       assert(Arg != nullptr);
40904eeddc0SDimitry Andric 
41004eeddc0SDimitry Andric       if (S->isElidable()) {
41104eeddc0SDimitry Andric         auto *ArgLoc = Env.getStorageLocation(*Arg, SkipPast::Reference);
41204eeddc0SDimitry Andric         if (ArgLoc == nullptr)
41304eeddc0SDimitry Andric           return;
41404eeddc0SDimitry Andric 
41504eeddc0SDimitry Andric         Env.setStorageLocation(*S, *ArgLoc);
41604eeddc0SDimitry Andric       } else if (auto *ArgVal = Env.getValue(*Arg, SkipPast::Reference)) {
41704eeddc0SDimitry Andric         auto &Loc = Env.createStorageLocation(*S);
41804eeddc0SDimitry Andric         Env.setStorageLocation(*S, Loc);
41904eeddc0SDimitry Andric         Env.setValue(Loc, *ArgVal);
42004eeddc0SDimitry Andric       }
42104eeddc0SDimitry Andric       return;
42204eeddc0SDimitry Andric     }
42304eeddc0SDimitry Andric 
42404eeddc0SDimitry Andric     auto &Loc = Env.createStorageLocation(*S);
42504eeddc0SDimitry Andric     Env.setStorageLocation(*S, Loc);
42604eeddc0SDimitry Andric     if (Value *Val = Env.createValue(S->getType()))
42704eeddc0SDimitry Andric       Env.setValue(Loc, *Val);
42804eeddc0SDimitry Andric   }
42904eeddc0SDimitry Andric 
43004eeddc0SDimitry Andric   void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *S) {
43104eeddc0SDimitry Andric     if (S->getOperator() == OO_Equal) {
43204eeddc0SDimitry Andric       assert(S->getNumArgs() == 2);
43304eeddc0SDimitry Andric 
43404eeddc0SDimitry Andric       const Expr *Arg0 = S->getArg(0);
43504eeddc0SDimitry Andric       assert(Arg0 != nullptr);
43604eeddc0SDimitry Andric 
43704eeddc0SDimitry Andric       const Expr *Arg1 = S->getArg(1);
43804eeddc0SDimitry Andric       assert(Arg1 != nullptr);
43904eeddc0SDimitry Andric 
44004eeddc0SDimitry Andric       // Evaluate only copy and move assignment operators.
44104eeddc0SDimitry Andric       auto *Arg0Type = Arg0->getType()->getUnqualifiedDesugaredType();
44204eeddc0SDimitry Andric       auto *Arg1Type = Arg1->getType()->getUnqualifiedDesugaredType();
44304eeddc0SDimitry Andric       if (Arg0Type != Arg1Type)
44404eeddc0SDimitry Andric         return;
44504eeddc0SDimitry Andric 
44604eeddc0SDimitry Andric       auto *ObjectLoc = Env.getStorageLocation(*Arg0, SkipPast::Reference);
44704eeddc0SDimitry Andric       if (ObjectLoc == nullptr)
44804eeddc0SDimitry Andric         return;
44904eeddc0SDimitry Andric 
45004eeddc0SDimitry Andric       auto *Val = Env.getValue(*Arg1, SkipPast::Reference);
45104eeddc0SDimitry Andric       if (Val == nullptr)
45204eeddc0SDimitry Andric         return;
45304eeddc0SDimitry Andric 
45481ad6265SDimitry Andric       // Assign a value to the storage location of the object.
45504eeddc0SDimitry Andric       Env.setValue(*ObjectLoc, *Val);
45681ad6265SDimitry Andric 
45781ad6265SDimitry Andric       // FIXME: Add a test for the value of the whole expression.
45881ad6265SDimitry Andric       // Assign a storage location for the whole expression.
45981ad6265SDimitry Andric       Env.setStorageLocation(*S, *ObjectLoc);
46004eeddc0SDimitry Andric     }
46104eeddc0SDimitry Andric   }
46204eeddc0SDimitry Andric 
46304eeddc0SDimitry Andric   void VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *S) {
46404eeddc0SDimitry Andric     if (S->getCastKind() == CK_ConstructorConversion) {
46504eeddc0SDimitry Andric       const Expr *SubExpr = S->getSubExpr();
46604eeddc0SDimitry Andric       assert(SubExpr != nullptr);
46704eeddc0SDimitry Andric 
46804eeddc0SDimitry Andric       auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
46904eeddc0SDimitry Andric       if (SubExprLoc == nullptr)
47004eeddc0SDimitry Andric         return;
47104eeddc0SDimitry Andric 
47204eeddc0SDimitry Andric       Env.setStorageLocation(*S, *SubExprLoc);
47304eeddc0SDimitry Andric     }
47404eeddc0SDimitry Andric   }
47504eeddc0SDimitry Andric 
47604eeddc0SDimitry Andric   void VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *S) {
47704eeddc0SDimitry Andric     auto &Loc = Env.createStorageLocation(*S);
47804eeddc0SDimitry Andric     Env.setStorageLocation(*S, Loc);
47904eeddc0SDimitry Andric     if (Value *Val = Env.createValue(S->getType()))
48004eeddc0SDimitry Andric       Env.setValue(Loc, *Val);
48104eeddc0SDimitry Andric   }
48204eeddc0SDimitry Andric 
48304eeddc0SDimitry Andric   void VisitCallExpr(const CallExpr *S) {
48481ad6265SDimitry Andric     // Of clang's builtins, only `__builtin_expect` is handled explicitly, since
48581ad6265SDimitry Andric     // others (like trap, debugtrap, and unreachable) are handled by CFG
48681ad6265SDimitry Andric     // construction.
48704eeddc0SDimitry Andric     if (S->isCallToStdMove()) {
48804eeddc0SDimitry Andric       assert(S->getNumArgs() == 1);
48904eeddc0SDimitry Andric 
49004eeddc0SDimitry Andric       const Expr *Arg = S->getArg(0);
49104eeddc0SDimitry Andric       assert(Arg != nullptr);
49204eeddc0SDimitry Andric 
49304eeddc0SDimitry Andric       auto *ArgLoc = Env.getStorageLocation(*Arg, SkipPast::None);
49404eeddc0SDimitry Andric       if (ArgLoc == nullptr)
49504eeddc0SDimitry Andric         return;
49604eeddc0SDimitry Andric 
49704eeddc0SDimitry Andric       Env.setStorageLocation(*S, *ArgLoc);
49881ad6265SDimitry Andric     } else if (S->getDirectCallee() != nullptr &&
49981ad6265SDimitry Andric                S->getDirectCallee()->getBuiltinID() ==
50081ad6265SDimitry Andric                    Builtin::BI__builtin_expect) {
50181ad6265SDimitry Andric       assert(S->getNumArgs() > 0);
50281ad6265SDimitry Andric       assert(S->getArg(0) != nullptr);
50381ad6265SDimitry Andric       // `__builtin_expect` returns by-value, so strip away any potential
50481ad6265SDimitry Andric       // references in the argument.
50581ad6265SDimitry Andric       auto *ArgLoc = Env.getStorageLocation(*S->getArg(0), SkipPast::Reference);
50681ad6265SDimitry Andric       if (ArgLoc == nullptr)
50781ad6265SDimitry Andric         return;
50881ad6265SDimitry Andric       Env.setStorageLocation(*S, *ArgLoc);
509*972a253aSDimitry Andric     } else if (const FunctionDecl *F = S->getDirectCallee()) {
510*972a253aSDimitry Andric       // This case is for context-sensitive analysis, which we only do if we
511*972a253aSDimitry Andric       // have the callee body available in the translation unit.
512*972a253aSDimitry Andric       if (!Options.ContextSensitive || F->getBody() == nullptr)
513*972a253aSDimitry Andric         return;
514*972a253aSDimitry Andric 
515*972a253aSDimitry Andric       auto &ASTCtx = F->getASTContext();
516*972a253aSDimitry Andric 
517*972a253aSDimitry Andric       // FIXME: Cache these CFGs.
518*972a253aSDimitry Andric       auto CFCtx = ControlFlowContext::build(F, F->getBody(), &ASTCtx);
519*972a253aSDimitry Andric       // FIXME: Handle errors here and below.
520*972a253aSDimitry Andric       assert(CFCtx);
521*972a253aSDimitry Andric       auto ExitBlock = CFCtx->getCFG().getExit().getBlockID();
522*972a253aSDimitry Andric 
523*972a253aSDimitry Andric       auto CalleeEnv = Env.pushCall(S);
524*972a253aSDimitry Andric 
525*972a253aSDimitry Andric       // FIXME: Use the same analysis as the caller for the callee.
526*972a253aSDimitry Andric       DataflowAnalysisOptions Options;
527*972a253aSDimitry Andric       auto Analysis = NoopAnalysis(ASTCtx, Options);
528*972a253aSDimitry Andric 
529*972a253aSDimitry Andric       auto BlockToOutputState =
530*972a253aSDimitry Andric           dataflow::runDataflowAnalysis(*CFCtx, Analysis, CalleeEnv);
531*972a253aSDimitry Andric       assert(BlockToOutputState);
532*972a253aSDimitry Andric       assert(ExitBlock < BlockToOutputState->size());
533*972a253aSDimitry Andric 
534*972a253aSDimitry Andric       auto ExitState = (*BlockToOutputState)[ExitBlock];
535*972a253aSDimitry Andric       assert(ExitState);
536*972a253aSDimitry Andric 
537*972a253aSDimitry Andric       Env = ExitState->Env;
53804eeddc0SDimitry Andric     }
53904eeddc0SDimitry Andric   }
54004eeddc0SDimitry Andric 
54104eeddc0SDimitry Andric   void VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *S) {
54204eeddc0SDimitry Andric     const Expr *SubExpr = S->getSubExpr();
54304eeddc0SDimitry Andric     assert(SubExpr != nullptr);
54404eeddc0SDimitry Andric 
54504eeddc0SDimitry Andric     auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
54604eeddc0SDimitry Andric     if (SubExprLoc == nullptr)
54704eeddc0SDimitry Andric       return;
54804eeddc0SDimitry Andric 
54904eeddc0SDimitry Andric     Env.setStorageLocation(*S, *SubExprLoc);
55004eeddc0SDimitry Andric   }
55104eeddc0SDimitry Andric 
55204eeddc0SDimitry Andric   void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *S) {
55304eeddc0SDimitry Andric     const Expr *SubExpr = S->getSubExpr();
55404eeddc0SDimitry Andric     assert(SubExpr != nullptr);
55504eeddc0SDimitry Andric 
55604eeddc0SDimitry Andric     auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
55704eeddc0SDimitry Andric     if (SubExprLoc == nullptr)
55804eeddc0SDimitry Andric       return;
55904eeddc0SDimitry Andric 
56004eeddc0SDimitry Andric     Env.setStorageLocation(*S, *SubExprLoc);
56104eeddc0SDimitry Andric   }
56204eeddc0SDimitry Andric 
56304eeddc0SDimitry Andric   void VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) {
56404eeddc0SDimitry Andric     if (S->getCastKind() == CK_NoOp) {
56504eeddc0SDimitry Andric       const Expr *SubExpr = S->getSubExpr();
56604eeddc0SDimitry Andric       assert(SubExpr != nullptr);
56704eeddc0SDimitry Andric 
56804eeddc0SDimitry Andric       auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None);
56904eeddc0SDimitry Andric       if (SubExprLoc == nullptr)
57004eeddc0SDimitry Andric         return;
57104eeddc0SDimitry Andric 
57204eeddc0SDimitry Andric       Env.setStorageLocation(*S, *SubExprLoc);
57304eeddc0SDimitry Andric     }
57404eeddc0SDimitry Andric   }
57504eeddc0SDimitry Andric 
57604eeddc0SDimitry Andric   void VisitConditionalOperator(const ConditionalOperator *S) {
57704eeddc0SDimitry Andric     // FIXME: Revisit this once flow conditions are added to the framework. For
57804eeddc0SDimitry Andric     // `a = b ? c : d` we can add `b => a == c && !b => a == d` to the flow
57904eeddc0SDimitry Andric     // condition.
58004eeddc0SDimitry Andric     auto &Loc = Env.createStorageLocation(*S);
58104eeddc0SDimitry Andric     Env.setStorageLocation(*S, Loc);
58204eeddc0SDimitry Andric     if (Value *Val = Env.createValue(S->getType()))
58304eeddc0SDimitry Andric       Env.setValue(Loc, *Val);
58404eeddc0SDimitry Andric   }
58504eeddc0SDimitry Andric 
58604eeddc0SDimitry Andric   void VisitInitListExpr(const InitListExpr *S) {
58704eeddc0SDimitry Andric     QualType Type = S->getType();
58804eeddc0SDimitry Andric 
58904eeddc0SDimitry Andric     auto &Loc = Env.createStorageLocation(*S);
59004eeddc0SDimitry Andric     Env.setStorageLocation(*S, Loc);
59104eeddc0SDimitry Andric 
59204eeddc0SDimitry Andric     auto *Val = Env.createValue(Type);
59304eeddc0SDimitry Andric     if (Val == nullptr)
59404eeddc0SDimitry Andric       return;
59504eeddc0SDimitry Andric 
59604eeddc0SDimitry Andric     Env.setValue(Loc, *Val);
59704eeddc0SDimitry Andric 
59804eeddc0SDimitry Andric     if (Type->isStructureOrClassType()) {
599*972a253aSDimitry Andric       for (auto It : llvm::zip(Type->getAsRecordDecl()->fields(), S->inits())) {
600*972a253aSDimitry Andric         const FieldDecl *Field = std::get<0>(It);
60104eeddc0SDimitry Andric         assert(Field != nullptr);
60204eeddc0SDimitry Andric 
603*972a253aSDimitry Andric         const Expr *Init = std::get<1>(It);
60404eeddc0SDimitry Andric         assert(Init != nullptr);
60504eeddc0SDimitry Andric 
60604eeddc0SDimitry Andric         if (Value *InitVal = Env.getValue(*Init, SkipPast::None))
60704eeddc0SDimitry Andric           cast<StructValue>(Val)->setChild(*Field, *InitVal);
60804eeddc0SDimitry Andric       }
60904eeddc0SDimitry Andric     }
61004eeddc0SDimitry Andric     // FIXME: Implement array initialization.
61104eeddc0SDimitry Andric   }
61204eeddc0SDimitry Andric 
61304eeddc0SDimitry Andric   void VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *S) {
61404eeddc0SDimitry Andric     auto &Loc = Env.createStorageLocation(*S);
61504eeddc0SDimitry Andric     Env.setStorageLocation(*S, Loc);
61604eeddc0SDimitry Andric     Env.setValue(Loc, Env.getBoolLiteralValue(S->getValue()));
61704eeddc0SDimitry Andric   }
61804eeddc0SDimitry Andric 
61981ad6265SDimitry Andric   void VisitParenExpr(const ParenExpr *S) {
62081ad6265SDimitry Andric     // The CFG does not contain `ParenExpr` as top-level statements in basic
62181ad6265SDimitry Andric     // blocks, however manual traversal to sub-expressions may encounter them.
62281ad6265SDimitry Andric     // Redirect to the sub-expression.
62381ad6265SDimitry Andric     auto *SubExpr = S->getSubExpr();
62481ad6265SDimitry Andric     assert(SubExpr != nullptr);
62581ad6265SDimitry Andric     Visit(SubExpr);
62681ad6265SDimitry Andric   }
62781ad6265SDimitry Andric 
62881ad6265SDimitry Andric   void VisitExprWithCleanups(const ExprWithCleanups *S) {
62981ad6265SDimitry Andric     // The CFG does not contain `ExprWithCleanups` as top-level statements in
63081ad6265SDimitry Andric     // basic blocks, however manual traversal to sub-expressions may encounter
63181ad6265SDimitry Andric     // them. Redirect to the sub-expression.
63281ad6265SDimitry Andric     auto *SubExpr = S->getSubExpr();
63381ad6265SDimitry Andric     assert(SubExpr != nullptr);
63481ad6265SDimitry Andric     Visit(SubExpr);
63581ad6265SDimitry Andric   }
63681ad6265SDimitry Andric 
63704eeddc0SDimitry Andric private:
63881ad6265SDimitry Andric   BoolValue &getLogicOperatorSubExprValue(const Expr &SubExpr) {
63981ad6265SDimitry Andric     // `SubExpr` and its parent logic operator might be part of different basic
64081ad6265SDimitry Andric     // blocks. We try to access the value that is assigned to `SubExpr` in the
64181ad6265SDimitry Andric     // corresponding environment.
64281ad6265SDimitry Andric     if (const Environment *SubExprEnv = StmtToEnv.getEnvironment(SubExpr)) {
64381ad6265SDimitry Andric       if (auto *Val = dyn_cast_or_null<BoolValue>(
64481ad6265SDimitry Andric               SubExprEnv->getValue(SubExpr, SkipPast::Reference)))
64581ad6265SDimitry Andric         return *Val;
64681ad6265SDimitry Andric     }
64781ad6265SDimitry Andric 
64881ad6265SDimitry Andric     if (Env.getStorageLocation(SubExpr, SkipPast::None) == nullptr) {
64981ad6265SDimitry Andric       // Sub-expressions that are logic operators are not added in basic blocks
65081ad6265SDimitry Andric       // (e.g. see CFG for `bool d = a && (b || c);`). If `SubExpr` is a logic
65181ad6265SDimitry Andric       // operator, it may not have been evaluated and assigned a value yet. In
65281ad6265SDimitry Andric       // that case, we need to first visit `SubExpr` and then try to get the
65381ad6265SDimitry Andric       // value that gets assigned to it.
65481ad6265SDimitry Andric       Visit(&SubExpr);
65581ad6265SDimitry Andric     }
65681ad6265SDimitry Andric 
65781ad6265SDimitry Andric     if (auto *Val = dyn_cast_or_null<BoolValue>(
65881ad6265SDimitry Andric             Env.getValue(SubExpr, SkipPast::Reference)))
65981ad6265SDimitry Andric       return *Val;
66081ad6265SDimitry Andric 
66181ad6265SDimitry Andric     // If the value of `SubExpr` is still unknown, we create a fresh symbolic
66281ad6265SDimitry Andric     // boolean value for it.
66381ad6265SDimitry Andric     return Env.makeAtomicBoolValue();
66481ad6265SDimitry Andric   }
66581ad6265SDimitry Andric 
66681ad6265SDimitry Andric   const StmtToEnvMap &StmtToEnv;
66704eeddc0SDimitry Andric   Environment &Env;
668*972a253aSDimitry Andric   TransferOptions Options;
66904eeddc0SDimitry Andric };
67004eeddc0SDimitry Andric 
671*972a253aSDimitry Andric void transfer(const StmtToEnvMap &StmtToEnv, const Stmt &S, Environment &Env,
672*972a253aSDimitry Andric               TransferOptions Options) {
673*972a253aSDimitry Andric   TransferVisitor(StmtToEnv, Env, Options).Visit(&S);
67404eeddc0SDimitry Andric }
67504eeddc0SDimitry Andric 
67604eeddc0SDimitry Andric } // namespace dataflow
67704eeddc0SDimitry Andric } // namespace clang
678