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 @@ -87,11 +87,50 @@ } void VisitDeclStmt(const DeclStmt *S) { - // FIXME: Add support for group decls, e.g: `int a, b;` - if (S->isSingleDecl()) { - if (const auto *D = dyn_cast(S->getSingleDecl())) { - visitVarDecl(*D); + // Group decls are converted into single decls in the CFG so the cast below + // is safe. + const auto &D = *cast(S->getSingleDecl()); + auto &Loc = Env.createStorageLocation(D); + Env.setStorageLocation(D, Loc); + + const Expr *InitExpr = D.getInit(); + if (InitExpr == nullptr) { + // No initializer expression - associate `Loc` with a new value. + Env.initValueInStorageLocation(Loc, D.getType()); + return; + } + + // The CFG does not contain `ParenExpr` as top-level statements in basic + // blocks, however sub-expressions can still be of that type. + InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens()); + assert(InitExpr != nullptr); + + if (D.getType()->isReferenceType()) { + // Initializing a reference variable - do not create a reference to + // reference. + if (auto *InitExprLoc = + Env.getStorageLocation(*InitExpr, SkipPast::Reference)) { + auto &Val = + Env.takeOwnership(std::make_unique(*InitExprLoc)); + Env.setValue(Loc, Val); + } else { + // FIXME: The initializer expression must always be assigned a value. + // Replace this with an assert when we have sufficient coverage of + // language features. + Env.initValueInStorageLocation(Loc, D.getType()); } + return; + } + + if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) { + Env.setValue(Loc, *InitExprVal); + } else if (!D.getType()->isStructureOrClassType()) { + // FIXME: The initializer expression must always be assigned a value. + // Replace this with an assert when we have sufficient coverage of + // language features. + Env.initValueInStorageLocation(Loc, D.getType()); + } else { + llvm_unreachable("structs and classes must always be assigned values"); } } @@ -309,57 +348,34 @@ Env.setStorageLocation(*S, *SubExprLoc); } - // FIXME: Add support for: - // - CXXBindTemporaryExpr - // - CXXBoolLiteralExpr - // - CXXStaticCastExpr - -private: - void visitVarDecl(const VarDecl &D) { - auto &Loc = Env.createStorageLocation(D); - Env.setStorageLocation(D, Loc); + void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *S) { + const Expr *SubExpr = S->getSubExpr(); + assert(SubExpr != nullptr); - const Expr *InitExpr = D.getInit(); - if (InitExpr == nullptr) { - // No initializer expression - associate `Loc` with a new value. - Env.initValueInStorageLocation(Loc, D.getType()); + auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None); + if (SubExprLoc == nullptr) return; - } - // The CFG does not contain `ParenExpr` as top-level statements in basic - // blocks, however sub-expressions can still be of that type. - InitExpr = skipExprWithCleanups(D.getInit()->IgnoreParens()); - assert(InitExpr != nullptr); + Env.setStorageLocation(*S, *SubExprLoc); + } - if (D.getType()->isReferenceType()) { - // Initializing a reference variable - do not create a reference to - // reference. - if (auto *InitExprLoc = - Env.getStorageLocation(*InitExpr, SkipPast::Reference)) { - auto &Val = - Env.takeOwnership(std::make_unique(*InitExprLoc)); - Env.setValue(Loc, Val); - } else { - // FIXME: The initializer expression must always be assigned a value. - // Replace this with an assert when we have sufficient coverage of - // language features. - Env.initValueInStorageLocation(Loc, D.getType()); - } - return; - } + void VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) { + if (S->getCastKind() == CK_NoOp) { + const Expr *SubExpr = S->getSubExpr(); + assert(SubExpr != nullptr); - if (auto *InitExprVal = Env.getValue(*InitExpr, SkipPast::None)) { - Env.setValue(Loc, *InitExprVal); - } else if (!D.getType()->isStructureOrClassType()) { - // FIXME: The initializer expression must always be assigned a value. - // Replace this with an assert when we have sufficient coverage of - // language features. - Env.initValueInStorageLocation(Loc, D.getType()); - } else { - llvm_unreachable("structs and classes must always be assigned values"); + auto *SubExprLoc = Env.getStorageLocation(*SubExpr, SkipPast::None); + if (SubExprLoc == nullptr) + return; + + Env.setStorageLocation(*S, *SubExprLoc); } } + // FIXME: Add support for: + // - CXXBoolLiteralExpr + +private: Environment &Env; }; 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 @@ -489,6 +489,44 @@ }); } +TEST_F(TransferTest, MultipleVarsDecl) { + std::string Code = R"( + void target() { + int Foo, Bar; + (void)0; + // [[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 ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const StorageLocation *FooLoc = + Env.getStorageLocation(*FooDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(FooLoc)); + + const StorageLocation *BarLoc = + Env.getStorageLocation(*BarDecl, SkipPast::None); + ASSERT_TRUE(isa_and_nonnull(BarLoc)); + + const Value *FooVal = Env.getValue(*FooLoc); + EXPECT_TRUE(isa_and_nonnull(FooVal)); + + const Value *BarVal = Env.getValue(*BarLoc); + EXPECT_TRUE(isa_and_nonnull(BarVal)); + }); +} + TEST_F(TransferTest, JoinVarDecl) { std::string Code = R"( void target(bool B) { @@ -1589,4 +1627,71 @@ }); } +TEST_F(TransferTest, BindTemporary) { + std::string Code = R"( + struct A { + virtual ~A() = default; + + int Baz; + }; + + void target(A Foo) { + int Bar = A(Foo).Baz; + // [[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 ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const ValueDecl *BazDecl = findValueDecl(ASTCtx, "Baz"); + ASSERT_THAT(BazDecl, NotNull()); + + const auto &FooVal = + *cast(Env.getValue(*FooDecl, SkipPast::None)); + const auto *BarVal = + cast(Env.getValue(*BarDecl, SkipPast::None)); + EXPECT_EQ(BarVal, &FooVal.getChild(*BazDecl)); + }); +} + +TEST_F(TransferTest, StaticCast) { + std::string Code = R"( + void target(int Foo) { + int Bar = static_cast(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 ValueDecl *BarDecl = findValueDecl(ASTCtx, "Bar"); + ASSERT_THAT(BarDecl, NotNull()); + + const auto *FooVal = + cast(Env.getValue(*FooDecl, SkipPast::None)); + const auto *BarVal = + cast(Env.getValue(*BarDecl, SkipPast::None)); + EXPECT_EQ(FooVal, BarVal); + }); +} + } // namespace