diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowAnalysisContext.h @@ -16,6 +16,7 @@ #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWANALYSISCONTEXT_H #include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" #include "clang/Analysis/FlowSensitive/StorageLocation.h" #include "clang/Analysis/FlowSensitive/Value.h" #include "llvm/ADT/DenseMap.h" @@ -70,6 +71,23 @@ return It == DeclToLoc.end() ? nullptr : It->second; } + /// Assigns `Loc` as the storage location of `E`. + /// + /// Requirements: + /// + /// `E` must not be assigned a storage location. + void setStorageLocation(const Expr &E, StorageLocation &Loc) { + assert(ExprToLoc.find(&E) == ExprToLoc.end()); + ExprToLoc[&E] = &Loc; + } + + /// Returns the storage location assigned to `E` or null if `E` has no + /// assigned storage location. + StorageLocation *getStorageLocation(const Expr &E) const { + auto It = ExprToLoc.find(&E); + return It == ExprToLoc.end() ? nullptr : It->second; + } + private: // Storage for the state of a program. std::vector> Locs; @@ -81,7 +99,7 @@ // basic blocks are evaluated multiple times. The storage locations that are // in scope for a particular basic block are stored in `Environment`. llvm::DenseMap DeclToLoc; - // FIXME: Add `Expr` to `StorageLocation` map. + llvm::DenseMap ExprToLoc; // FIXME: Add `StorageLocation` for `this`. diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -16,6 +16,7 @@ #define LLVM_CLANG_ANALYSIS_FLOWSENSITIVE_DATAFLOWENVIRONMENT_H #include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/AST/TypeOrdering.h" #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" @@ -28,6 +29,15 @@ namespace clang { namespace dataflow { +/// Indicates what kind of indirections should be skipped past when retrieving +/// storage locations or values. +enum class SkipPast { + /// No indirections should be skipped past. + None, + /// An optional reference should be skipped past. + Reference, +}; + /// Holds the state of the program (store and heap) at a given program point. class Environment { public: @@ -50,6 +60,11 @@ /// returned storage location in the environment. StorageLocation &createStorageLocation(const VarDecl &D); + /// Creates a storage location for `E`. Does not assign the returned storage + /// location to `E` in the environment. Does not assign a value to the + /// returned storage location in the environment. + StorageLocation &createStorageLocation(const Expr &E); + /// Assigns `Loc` as the storage location of `D` in the environment. /// /// Requirements: @@ -57,9 +72,22 @@ /// `D` must not be assigned a storage location in the environment. void setStorageLocation(const ValueDecl &D, StorageLocation &Loc); - /// Returns the storage location assigned to `D` in the environment or null if - /// `D` isn't assigned a storage location in the environment. - StorageLocation *getStorageLocation(const ValueDecl &D) const; + /// Returns the storage location assigned to `D` in the environment, applying + /// the `SP` policy for skipping past indirections, or null if `D` isn't + /// assigned a storage location in the environment. + StorageLocation *getStorageLocation(const ValueDecl &D, SkipPast SP) const; + + /// Assigns `Loc` as the storage location of `E` in the environment. + /// + /// Requirements: + /// + /// `E` must not be assigned a storage location in the environment. + void setStorageLocation(const Expr &E, StorageLocation &Loc); + + /// Returns the storage location assigned to `E` in the environment, applying + /// the `SP` policy for skipping past indirections, or null if `E` isn't + /// assigned a storage location in the environment. + StorageLocation *getStorageLocation(const Expr &E, SkipPast SP) const; /// Creates a value appropriate for `Type`, assigns it to `Loc`, and returns /// it, if `Type` is supported, otherwise return null. If `Type` is a pointer @@ -74,9 +102,26 @@ /// Assigns `Val` as the value of `Loc` in the environment. void setValue(const StorageLocation &Loc, Value &Val); - /// Returns the value assigned to `Loc` in the environment or null if `Loc` - /// isn't assigned a value in the environment. - Value *getValue(const StorageLocation &Loc) const; + /// Returns the value assigned to `Loc` in the environment, applying the `SP` + /// policy for skipping past indirections, or null if `Loc` isn't assigned a + /// value in the environment. + Value *getValue(const StorageLocation &Loc, SkipPast SP) const; + + /// Equivalent to `getValue(getStorageLocation(D, SP), SkipPast::None)` if `D` + /// is assigned a storage location in the environment, otherwise returns null. + Value *getValue(const ValueDecl &D, SkipPast SP) const; + + /// Equivalent to `getValue(getStorageLocation(E, SP), SkipPast::None)` if `E` + /// is assigned a storage location in the environment, otherwise returns null. + Value *getValue(const Expr &E, SkipPast SP) const; + + /// Transfers ownership of `Loc` to the analysis context and returns a + /// reference to `Loc`. + StorageLocation &takeOwnership(std::unique_ptr Loc); + + /// Transfers ownership of `Val` to the analysis context and returns a + /// reference to `Val`. + Value &takeOwnership(std::unique_ptr Val); private: /// Returns the value assigned to `Loc` in the environment or null if `Type` @@ -94,6 +139,10 @@ const StorageLocation &Loc, QualType Type, llvm::DenseSet &Visited); + StorageLocation &skip(StorageLocation &Loc, SkipPast SP) const; + const StorageLocation &skip(const StorageLocation &Loc, SkipPast SP) const; + + // `DACtx` is not null and not owned by this object. DataflowAnalysisContext *DACtx; // Maps from program declarations and statements to storage locations that are @@ -101,7 +150,7 @@ // include only storage locations that are in scope for a particular basic // block. llvm::DenseMap DeclToLoc; - // FIXME: Add `Expr` to `StorageLocation` map. + llvm::DenseMap ExprToLoc; llvm::DenseMap LocToVal; diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -20,6 +20,7 @@ #include "clang/Analysis/FlowSensitive/Value.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/Support/ErrorHandling.h" #include #include @@ -73,10 +74,10 @@ for (const FieldDecl *Field : Type->getAsRecordDecl()->fields()) { FieldLocs.insert({Field, &createStorageLocation(Field->getType())}); } - return DACtx->takeOwnership( + return takeOwnership( std::make_unique(Type, std::move(FieldLocs))); } - return DACtx->takeOwnership(std::make_unique(Type)); + return takeOwnership(std::make_unique(Type)); } StorageLocation &Environment::createStorageLocation(const VarDecl &D) { @@ -90,25 +91,62 @@ return Loc; } +StorageLocation &Environment::createStorageLocation(const Expr &E) { + // Evaluated expressions are always assigned the same storage locations to + // ensure that the environment stabilizes across loop iterations. Storage + // locations for evaluated expressions are stored in the analysis context. + if (auto *Loc = DACtx->getStorageLocation(E)) + return *Loc; + auto &Loc = createStorageLocation(E.getType()); + DACtx->setStorageLocation(E, Loc); + return Loc; +} + void Environment::setStorageLocation(const ValueDecl &D, StorageLocation &Loc) { assert(DeclToLoc.find(&D) == DeclToLoc.end()); DeclToLoc[&D] = &Loc; } -StorageLocation *Environment::getStorageLocation(const ValueDecl &D) const { +StorageLocation *Environment::getStorageLocation(const ValueDecl &D, + SkipPast SP) const { auto It = DeclToLoc.find(&D); - return It == DeclToLoc.end() ? nullptr : It->second; + return It == DeclToLoc.end() ? nullptr : &skip(*It->second, SP); +} + +void Environment::setStorageLocation(const Expr &E, StorageLocation &Loc) { + assert(ExprToLoc.find(&E) == ExprToLoc.end()); + ExprToLoc[&E] = &Loc; +} + +StorageLocation *Environment::getStorageLocation(const Expr &E, + SkipPast SP) const { + auto It = ExprToLoc.find(&E); + return It == ExprToLoc.end() ? nullptr : &skip(*It->second, SP); } void Environment::setValue(const StorageLocation &Loc, Value &Value) { LocToVal[&Loc] = &Value; } -Value *Environment::getValue(const StorageLocation &Loc) const { - auto It = LocToVal.find(&Loc); +Value *Environment::getValue(const StorageLocation &Loc, SkipPast SP) const { + auto It = LocToVal.find(&skip(Loc, SP)); return It == LocToVal.end() ? nullptr : It->second; } +Value *Environment::getValue(const ValueDecl &D, SkipPast SP) const { + auto *Loc = getStorageLocation(D, SP); + if (Loc == nullptr) + return nullptr; + return getValue(*Loc, SkipPast::None); +} + +Value *Environment::getValue(const Expr &E, SkipPast SP) const { + auto *Loc = getStorageLocation(E, SP); + if (Loc == nullptr) + return nullptr; + return getValue(*Loc, SkipPast::None); +} + Value *Environment::initValueInStorageLocation(const StorageLocation &Loc, QualType Type) { llvm::DenseSet Visited; @@ -121,9 +159,9 @@ assert(!Type.isNull()); if (Type->isIntegerType()) { - auto &Value = DACtx->takeOwnership(std::make_unique()); - setValue(Loc, Value); - return &Value; + auto &Val = takeOwnership(std::make_unique()); + setValue(Loc, Val); + return &Val; } if (Type->isReferenceType()) { @@ -137,10 +175,9 @@ Visited.erase(PointeeType.getCanonicalType()); } - auto &Value = - DACtx->takeOwnership(std::make_unique(PointeeLoc)); - setValue(Loc, Value); - return &Value; + auto &Val = takeOwnership(std::make_unique(PointeeLoc)); + setValue(Loc, Val); + return &Val; } if (Type->isPointerType()) { @@ -154,10 +191,9 @@ Visited.erase(PointeeType.getCanonicalType()); } - auto &Value = - DACtx->takeOwnership(std::make_unique(PointeeLoc)); - setValue(Loc, Value); - return &Value; + auto &Val = takeOwnership(std::make_unique(PointeeLoc)); + setValue(Loc, Val); + return &Val; } if (Type->isStructureOrClassType()) { @@ -178,14 +214,45 @@ Visited.erase(FieldType.getCanonicalType()); } - auto &Value = DACtx->takeOwnership( - std::make_unique(std::move(FieldValues))); - setValue(Loc, Value); - return &Value; + auto &Val = + takeOwnership(std::make_unique(std::move(FieldValues))); + setValue(Loc, Val); + return &Val; } return nullptr; } +StorageLocation & +Environment::takeOwnership(std::unique_ptr Loc) { + return DACtx->takeOwnership(std::move(Loc)); +} + +Value &Environment::takeOwnership(std::unique_ptr Val) { + return DACtx->takeOwnership(std::move(Val)); +} + +StorageLocation &Environment::skip(StorageLocation &Loc, SkipPast SP) const { + switch (SP) { + case SkipPast::None: + return Loc; + case SkipPast::Reference: + // References cannot be chained so we only need to skip past one level of + // indirection. + if (auto *Val = + dyn_cast_or_null(getValue(Loc, SkipPast::None))) { + return Val->getPointeeLoc(); + } else { + return Loc; + } + } + llvm_unreachable("bad SkipPast kind"); +} + +const StorageLocation &Environment::skip(const StorageLocation &Loc, + SkipPast SP) const { + return skip(*const_cast(&Loc), SP); +} + } // namespace dataflow } // namespace clang diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -15,11 +15,14 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/Expr.h" +#include "clang/AST/OperationKinds.h" #include "clang/AST/Stmt.h" #include "clang/AST/StmtVisitor.h" #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" +#include "clang/Basic/OperatorKinds.h" #include "llvm/Support/Casting.h" #include +#include namespace clang { namespace dataflow { @@ -28,6 +31,39 @@ public: TransferVisitor(Environment &Env) : Env(Env) {} + void VisitBinaryOperator(const BinaryOperator *S) { + if (S->getOpcode() == BO_Assign) { + assert(S->getLHS() != nullptr); + auto *LHSLoc = Env.getStorageLocation(*S->getLHS(), SkipPast::Reference); + if (LHSLoc == nullptr) + return; + + assert(S->getRHS() != nullptr); + Value *RHSVal = Env.getValue(*S->getRHS(), SkipPast::Reference); + if (RHSVal == nullptr) + return; + + Env.setValue(*LHSLoc, *RHSVal); + } + // FIXME: Add support for BO_EQ, BO_NE. + } + + void VisitDeclRefExpr(const DeclRefExpr *S) { + assert(S->getDecl() != nullptr); + auto *DeclLoc = Env.getStorageLocation(*S->getDecl(), SkipPast::None); + if (DeclLoc == nullptr) + return; + + if (S->getDecl()->getType()->isReferenceType()) { + Env.setStorageLocation(*S, *DeclLoc); + } else { + auto &Loc = Env.createStorageLocation(*S); + auto &Val = Env.takeOwnership(std::make_unique(*DeclLoc)); + Env.setStorageLocation(*S, Loc); + Env.setValue(Loc, Val); + } + } + void VisitDeclStmt(const DeclStmt *S) { // FIXME: Add support for group decls, e.g: `int a, b;` if (S->isSingleDecl()) { @@ -37,8 +73,22 @@ } } + void VisitImplicitCastExpr(const ImplicitCastExpr *S) { + if (S->getCastKind() == CK_LValueToRValue) { + assert(S->getSubExpr() != nullptr); + auto *SubExprVal = Env.getValue(*S->getSubExpr(), SkipPast::Reference); + if (SubExprVal == nullptr) + return; + + auto &ExprLoc = Env.createStorageLocation(*S); + Env.setStorageLocation(*S, ExprLoc); + Env.setValue(ExprLoc, *SubExprVal); + } + // FIXME: Add support for CK_NoOp, CK_UserDefinedConversion, + // CK_ConstructorConversion, CK_UncheckedDerivedToBase. + } + // FIXME: Add support for: - // - BinaryOperator // - CallExpr // - CXXBindTemporaryExpr // - CXXBoolLiteralExpr @@ -47,8 +97,6 @@ // - CXXOperatorCallExpr // - CXXStaticCastExpr // - CXXThisExpr - // - DeclRefExpr - // - ImplicitCastExpr // - MaterializeTemporaryExpr // - MemberExpr // - UnaryOperator @@ -57,6 +105,24 @@ void visitVarDecl(const VarDecl &D) { auto &Loc = Env.createStorageLocation(D); Env.setStorageLocation(D, Loc); + + if (const Expr *InitExpr = D.getInit()) { + if (D.getType()->isReferenceType()) { + if (auto *InitExprLoc = + Env.getStorageLocation(*InitExpr, SkipPast::Reference)) { + auto &Val = + Env.takeOwnership(std::make_unique(*InitExprLoc)); + Env.setValue(Loc, Val); + return; + } + } else { + if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) { + Env.setValue(Loc, *InitExprVal); + return; + } + } + } + Env.initValueInStorageLocation(Loc, D.getType()); } diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -82,10 +82,11 @@ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); ASSERT_THAT(FooDecl, NotNull()); - const StorageLocation *FooLoc = Env.getStorageLocation(*FooDecl); + const StorageLocation *FooLoc = + Env.getStorageLocation(*FooDecl, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(FooLoc)); - const Value *FooVal = Env.getValue(*FooLoc); + const Value *FooVal = Env.getValue(*FooLoc, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(FooVal)); }); } @@ -125,14 +126,15 @@ } ASSERT_THAT(BarDecl, NotNull()); - const auto *FooLoc = - cast(Env.getStorageLocation(*FooDecl)); + const auto *FooLoc = cast( + Env.getStorageLocation(*FooDecl, SkipPast::None)); const auto *BarLoc = cast(&FooLoc->getChild(*BarDecl)); - const auto *FooVal = cast(Env.getValue(*FooLoc)); + const auto *FooVal = + cast(Env.getValue(*FooLoc, SkipPast::None)); const auto *BarVal = cast(&FooVal->getChild(*BarDecl)); - ASSERT_EQ(Env.getValue(*BarLoc), BarVal); + ASSERT_EQ(Env.getValue(*BarLoc, SkipPast::None), BarVal); }); } @@ -171,14 +173,15 @@ } ASSERT_THAT(BarDecl, NotNull()); - const auto *FooLoc = - cast(Env.getStorageLocation(*FooDecl)); + const auto *FooLoc = cast( + Env.getStorageLocation(*FooDecl, SkipPast::None)); const auto *BarLoc = cast(&FooLoc->getChild(*BarDecl)); - const auto *FooVal = cast(Env.getValue(*FooLoc)); + const auto *FooVal = + cast(Env.getValue(*FooLoc, SkipPast::None)); const auto *BarVal = cast(&FooVal->getChild(*BarDecl)); - ASSERT_EQ(Env.getValue(*BarLoc), BarVal); + ASSERT_EQ(Env.getValue(*BarLoc, SkipPast::None), BarVal); }); } @@ -204,15 +207,17 @@ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); ASSERT_THAT(FooDecl, NotNull()); - const StorageLocation *FooLoc = Env.getStorageLocation(*FooDecl); + const StorageLocation *FooLoc = + Env.getStorageLocation(*FooDecl, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(FooLoc)); const ReferenceValue *FooVal = - cast(Env.getValue(*FooLoc)); + cast(Env.getValue(*FooLoc, SkipPast::None)); const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc(); ASSERT_TRUE(isa(&FooPointeeLoc)); - const Value *FooPointeeVal = Env.getValue(FooPointeeLoc); + const Value *FooPointeeVal = + Env.getValue(FooPointeeLoc, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(FooPointeeVal)); }); } @@ -293,36 +298,37 @@ ASSERT_THAT(BazRefDecl, NotNull()); ASSERT_THAT(BazPtrDecl, NotNull()); - const auto *FooLoc = - cast(Env.getStorageLocation(*FooDecl)); - const auto *FooVal = cast(Env.getValue(*FooLoc)); - const auto *FooPointeeVal = - cast(Env.getValue(FooVal->getPointeeLoc())); + const auto *FooLoc = cast( + Env.getStorageLocation(*FooDecl, SkipPast::None)); + const auto *FooVal = + cast(Env.getValue(*FooLoc, SkipPast::None)); + const auto *FooPointeeVal = cast( + Env.getValue(FooVal->getPointeeLoc(), SkipPast::None)); const auto *BarVal = cast(&FooPointeeVal->getChild(*BarDecl)); - const auto *BarPointeeVal = - cast(Env.getValue(BarVal->getPointeeLoc())); + const auto *BarPointeeVal = cast( + Env.getValue(BarVal->getPointeeLoc(), SkipPast::None)); const auto *FooRefVal = cast(&BarPointeeVal->getChild(*FooRefDecl)); const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc(); - ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull()); + ASSERT_THAT(Env.getValue(FooRefPointeeLoc, SkipPast::None), IsNull()); const auto *FooPtrVal = cast(&BarPointeeVal->getChild(*FooPtrDecl)); const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc(); - ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull()); + ASSERT_THAT(Env.getValue(FooPtrPointeeLoc, SkipPast::None), IsNull()); const auto *BazRefVal = cast(&BarPointeeVal->getChild(*BazRefDecl)); const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc(); - ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull()); + ASSERT_THAT(Env.getValue(BazRefPointeeLoc, SkipPast::None), NotNull()); const auto *BazPtrVal = cast(&BarPointeeVal->getChild(*BazPtrDecl)); const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc(); - ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull()); + ASSERT_THAT(Env.getValue(BazPtrPointeeLoc, SkipPast::None), NotNull()); }); } @@ -348,14 +354,17 @@ const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); ASSERT_THAT(FooDecl, NotNull()); - const StorageLocation *FooLoc = Env.getStorageLocation(*FooDecl); + const StorageLocation *FooLoc = + Env.getStorageLocation(*FooDecl, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(FooLoc)); - const PointerValue *FooVal = cast(Env.getValue(*FooLoc)); + const PointerValue *FooVal = + cast(Env.getValue(*FooLoc, SkipPast::None)); const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc(); ASSERT_TRUE(isa(&FooPointeeLoc)); - const Value *FooPointeeVal = Env.getValue(FooPointeeLoc); + const Value *FooPointeeVal = + Env.getValue(FooPointeeLoc, SkipPast::None); ASSERT_TRUE(isa_and_nonnull(FooPointeeVal)); }); } @@ -449,36 +458,37 @@ ASSERT_THAT(BazRefDecl, NotNull()); ASSERT_THAT(BazPtrDecl, NotNull()); - const auto *FooLoc = - cast(Env.getStorageLocation(*FooDecl)); - const auto *FooVal = cast(Env.getValue(*FooLoc)); - const auto *FooPointeeVal = - cast(Env.getValue(FooVal->getPointeeLoc())); + const auto *FooLoc = cast( + Env.getStorageLocation(*FooDecl, SkipPast::None)); + const auto *FooVal = + cast(Env.getValue(*FooLoc, SkipPast::None)); + const auto *FooPointeeVal = cast( + Env.getValue(FooVal->getPointeeLoc(), SkipPast::None)); const auto *BarVal = cast(&FooPointeeVal->getChild(*BarDecl)); - const auto *BarPointeeVal = - cast(Env.getValue(BarVal->getPointeeLoc())); + const auto *BarPointeeVal = cast( + Env.getValue(BarVal->getPointeeLoc(), SkipPast::None)); const auto *FooRefVal = cast(&BarPointeeVal->getChild(*FooRefDecl)); const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc(); - ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull()); + ASSERT_THAT(Env.getValue(FooRefPointeeLoc, SkipPast::None), IsNull()); const auto *FooPtrVal = cast(&BarPointeeVal->getChild(*FooPtrDecl)); const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc(); - ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull()); + ASSERT_THAT(Env.getValue(FooPtrPointeeLoc, SkipPast::None), IsNull()); const auto *BazRefVal = cast(&BarPointeeVal->getChild(*BazRefDecl)); const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc(); - ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull()); + ASSERT_THAT(Env.getValue(BazRefPointeeLoc, SkipPast::None), NotNull()); const auto *BazPtrVal = cast(&BarPointeeVal->getChild(*BazPtrDecl)); const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc(); - ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull()); + ASSERT_THAT(Env.getValue(BazPtrPointeeLoc, SkipPast::None), NotNull()); }); } @@ -498,43 +508,148 @@ // [[p4]] } )"; - runDataflow( - Code, [](llvm::ArrayRef< - std::pair>> - Results, - ASTContext &ASTCtx) { - ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _), - Pair("p2", _), Pair("p1", _))); - const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); - ASSERT_THAT(FooDecl, NotNull()); + runDataflow(Code, [](llvm::ArrayRef>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _), + Pair("p2", _), Pair("p1", _))); + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + ASSERT_THAT(FooDecl, NotNull()); - const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); - ASSERT_THAT(BarDecl, NotNull()); + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); + ASSERT_THAT(BarDecl, NotNull()); - const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz"); - ASSERT_THAT(BazDecl, NotNull()); - - const Environment &Env1 = Results[3].second.Env; - const StorageLocation *FooLoc = Env1.getStorageLocation(*FooDecl); - ASSERT_THAT(FooLoc, NotNull()); - ASSERT_THAT(Env1.getStorageLocation(*BarDecl), IsNull()); - ASSERT_THAT(Env1.getStorageLocation(*BazDecl), IsNull()); - - const Environment &Env2 = Results[2].second.Env; - ASSERT_EQ(Env2.getStorageLocation(*FooDecl), FooLoc); - ASSERT_THAT(Env2.getStorageLocation(*BarDecl), NotNull()); - ASSERT_THAT(Env2.getStorageLocation(*BazDecl), IsNull()); - - const Environment &Env3 = Results[1].second.Env; - ASSERT_EQ(Env3.getStorageLocation(*FooDecl), FooLoc); - ASSERT_THAT(Env3.getStorageLocation(*BarDecl), IsNull()); - ASSERT_THAT(Env3.getStorageLocation(*BazDecl), NotNull()); - - const Environment &Env4 = Results[0].second.Env; - ASSERT_EQ(Env4.getStorageLocation(*FooDecl), FooLoc); - ASSERT_THAT(Env4.getStorageLocation(*BarDecl), IsNull()); - ASSERT_THAT(Env4.getStorageLocation(*BazDecl), IsNull()); - }); + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz"); + ASSERT_THAT(BazDecl, NotNull()); + + const Environment &Env1 = Results[3].second.Env; + const StorageLocation *FooLoc = + Env1.getStorageLocation(*FooDecl, SkipPast::None); + ASSERT_THAT(FooLoc, NotNull()); + ASSERT_THAT(Env1.getStorageLocation(*BarDecl, SkipPast::None), IsNull()); + ASSERT_THAT(Env1.getStorageLocation(*BazDecl, SkipPast::None), IsNull()); + + const Environment &Env2 = Results[2].second.Env; + ASSERT_EQ(Env2.getStorageLocation(*FooDecl, SkipPast::None), FooLoc); + ASSERT_THAT(Env2.getStorageLocation(*BarDecl, SkipPast::None), NotNull()); + ASSERT_THAT(Env2.getStorageLocation(*BazDecl, SkipPast::None), IsNull()); + + const Environment &Env3 = Results[1].second.Env; + ASSERT_EQ(Env3.getStorageLocation(*FooDecl, SkipPast::None), FooLoc); + ASSERT_THAT(Env3.getStorageLocation(*BarDecl, SkipPast::None), IsNull()); + ASSERT_THAT(Env3.getStorageLocation(*BazDecl, SkipPast::None), NotNull()); + + const Environment &Env4 = Results[0].second.Env; + ASSERT_EQ(Env4.getStorageLocation(*FooDecl, SkipPast::None), FooLoc); + ASSERT_THAT(Env4.getStorageLocation(*BarDecl, SkipPast::None), IsNull()); + ASSERT_THAT(Env4.getStorageLocation(*BazDecl, SkipPast::None), IsNull()); + }); +} + +TEST_F(TransferTest, BinaryOperatorAssign) { + std::string Code = R"( + void target() { + int foo; + int bar; + bar = foo; + // [[p]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(FooVal)); + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const Value *BarVal = Env.getValue(*BarDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(BarVal)); + + EXPECT_EQ(BarVal, FooVal); + }); +} + +TEST_F(TransferTest, VarDeclInitAssign) { + std::string Code = R"( + void target() { + int foo; + int bar = foo; + // [[p]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(FooVal)); + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const Value *BarVal = Env.getValue(*BarDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(BarVal)); + + EXPECT_EQ(BarVal, FooVal); + }); +} + +TEST_F(TransferTest, AssignToReference) { + std::string Code = R"( + void target() { + int foo; + int bar; + int& baz = foo; + // [[p1]] + baz = bar; + // [[p2]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _))); + const Environment &Env1 = Results[0].second.Env; + const Environment &Env2 = Results[1].second.Env; + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo"); + ASSERT_THAT(FooDecl, NotNull()); + + const Value *FooVal = Env1.getValue(*FooDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(FooVal)); + + const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const Value *BarVal = Env1.getValue(*BarDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(BarVal)); + + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz"); + ASSERT_THAT(BazDecl, NotNull()); + + EXPECT_EQ(Env1.getValue(*BazDecl, SkipPast::Reference), FooVal); + EXPECT_EQ(Env2.getValue(*BazDecl, SkipPast::Reference), BarVal); + }); } } // namespace