diff --git a/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h b/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h index b5bdff2df8ed6..03fa05f69ef1d 100644 --- a/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h +++ b/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h @@ -58,6 +58,8 @@ namespace clang::dataflow { /// for `std::optional`, we assume the (Matcher, TransferFunction) case /// with custom handling is ordered early so that these generic cases /// do not trigger. +ast_matchers::StatementMatcher isPointerLikeConstructor(); +ast_matchers::StatementMatcher isSmartPointerLikeConstructor(); ast_matchers::StatementMatcher isPointerLikeOperatorStar(); ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar(); ast_matchers::StatementMatcher isPointerLikeOperatorArrow(); @@ -80,6 +82,8 @@ isSmartPointerLikeGetMethodCall(clang::StringRef MethodName = "get"); const FunctionDecl * getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE); +const FunctionDecl * +getCanonicalSmartPointerLikeOperatorCalleeForType(const CXXRecordDecl *RD); /// A transfer function for `operator*` (and `value`) calls that can be /// cached. Runs the `InitializeLoc` callback to initialize any new /// StorageLocations. @@ -163,6 +167,20 @@ void transferSmartPointerLikeCachedDeref( State.Env.setStorageLocation(*DerefExpr, LocForValue); } +// This was introduced after the QualType was added to InitializeLoc, so +// we don't provide a compatibility wrapper. +template +void transferSmartPointerLikeConstructor( + const CXXConstructExpr *ConstructOperator, + RecordStorageLocation *SmartPointerLoc, TransferState &State, + llvm::function_ref InitializeLoc) { + const FunctionDecl *CanonicalCallee = + getCanonicalSmartPointerLikeOperatorCalleeForType( + ConstructOperator->getType()->getAsCXXRecordDecl()); + State.Lattice.getOrCreateConstMethodReturnStorageLocation( + *SmartPointerLoc, CanonicalCallee, State.Env, InitializeLoc); +} + template void transferSmartPointerLikeCachedGet( const CallExpr *GetExpr, RecordStorageLocation *SmartPointerLoc, diff --git a/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp b/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp index d87b2e6f03857..e639119e1a290 100644 --- a/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp +++ b/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp @@ -129,6 +129,12 @@ AST_MATCHER(clang::CXXRecordDecl, pointerClass) { namespace clang::dataflow { +ast_matchers::StatementMatcher isSmartPointerLikeConstructor() { + using namespace ast_matchers; + return cxxConstructExpr(hasType(hasCanonicalType(qualType( + hasDeclaration(cxxRecordDecl(smartPointerClassWithGetOrValue())))))); +} + ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar() { return cxxOperatorCallExpr( hasOverloadedOperatorName("*"), @@ -145,6 +151,12 @@ ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() { ofClass(smartPointerClassWithGetOrValue())))); } +ast_matchers::StatementMatcher isPointerLikeConstructor() { + using namespace ast_matchers; + return cxxConstructExpr(hasType(hasCanonicalType( + qualType(hasDeclaration(cxxRecordDecl(pointerClass())))))); +} + ast_matchers::StatementMatcher isPointerLikeOperatorStar() { return cxxOperatorCallExpr( hasOverloadedOperatorName("*"), @@ -177,15 +189,8 @@ isSmartPointerLikeGetMethodCall(clang::StringRef MethodName) { } const FunctionDecl * -getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE) { +getCanonicalSmartPointerLikeOperatorCalleeForType(const CXXRecordDecl *RD) { const FunctionDecl *CanonicalCallee = nullptr; - const CXXMethodDecl *Callee = - cast_or_null(CE->getDirectCallee()); - if (Callee == nullptr) - return nullptr; - const CXXRecordDecl *RD = Callee->getParent(); - if (RD == nullptr) - return nullptr; for (const auto *MD : RD->methods()) { if (MD->getOverloadedOperator() == OO_Star && MD->isConst() && MD->getNumParams() == 0 && MD->getReturnType()->isReferenceType()) { @@ -196,4 +201,16 @@ getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE) { return CanonicalCallee; } +const FunctionDecl * +getCanonicalSmartPointerLikeOperatorCallee(const CallExpr *CE) { + const CXXMethodDecl *Callee = + cast_or_null(CE->getDirectCallee()); + if (Callee == nullptr) + return nullptr; + const CXXRecordDecl *RD = Callee->getParent(); + if (RD == nullptr) + return nullptr; + return getCanonicalSmartPointerLikeOperatorCalleeForType(RD); +} + } // namespace clang::dataflow diff --git a/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp b/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp index 5d3d5b0cfea09..05c66b0847c7a 100644 --- a/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp @@ -64,6 +64,15 @@ TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrowGet) { isSmartPointerLikeOperatorArrow())); EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias P) { return P->i; }", isPointerLikeOperatorArrow())); + + EXPECT_TRUE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isSmartPointerLikeConstructor())); + EXPECT_TRUE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isPointerLikeConstructor())); } TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrow) { @@ -101,6 +110,15 @@ TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrow) { isSmartPointerLikeOperatorArrow())); EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias P) { return P->i; }", isPointerLikeOperatorArrow())); + + EXPECT_FALSE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isSmartPointerLikeConstructor())); + EXPECT_TRUE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isPointerLikeConstructor())); } TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) { @@ -141,6 +159,15 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) { EXPECT_TRUE(matches(Decls, "int target(std::unique_ptr P) { return P->i; }", isPointerLikeOperatorArrow())); + + EXPECT_FALSE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isSmartPointerLikeConstructor())); + EXPECT_FALSE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isPointerLikeConstructor())); } TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) { @@ -163,6 +190,15 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) { EXPECT_FALSE( matches(Decls, "int target(std::unique_ptr P) { return (P * 10).i; }", isPointerLikeOperatorStar())); + + EXPECT_FALSE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isSmartPointerLikeConstructor())); + EXPECT_FALSE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isPointerLikeConstructor())); } TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) { @@ -196,6 +232,15 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) { EXPECT_FALSE( matches(Decls, "int target(std::unique_ptr P) { return P.get()->i; }", isSmartPointerLikeGetMethodCall())); + + EXPECT_FALSE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isSmartPointerLikeConstructor())); + EXPECT_FALSE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isPointerLikeConstructor())); } TEST(SmartPointerAccessorCachingTest, NoMatchIfNoStarMethod) { @@ -221,6 +266,15 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoStarMethod) { EXPECT_FALSE(matches(Decls, "int target(std::unique_ptr P) { return P->i; }", isSmartPointerLikeGetMethodCall())); + + EXPECT_FALSE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isSmartPointerLikeConstructor())); + EXPECT_FALSE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isPointerLikeConstructor())); } TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) { @@ -276,6 +330,13 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) { Decls, "int target(const std::optional &Const) { return Const.value().i; }", isSmartPointerLikeValueMethodCall())); + + EXPECT_TRUE(matches( + Decls, "std::optional& Helper(); int target() { auto S = Helper(); }", + isSmartPointerLikeConstructor())); + EXPECT_TRUE(matches( + Decls, "std::optional& Helper(); int target() { auto S = Helper(); }", + isPointerLikeConstructor())); } TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) { @@ -329,6 +390,9 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) { EXPECT_TRUE(matches( Decls, "int target(const HasGetAndValue &Const) { return Const->i; }", isPointerLikeOperatorArrow())); + EXPECT_TRUE(matches( + Decls, "HasGetAndValue& Helper(); int target() { auto S = Helper(); }", + isPointerLikeConstructor())); EXPECT_TRUE(matches( Decls, @@ -346,6 +410,9 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) { Decls, "int target(const HasGetAndValue &Const) { return Const.get()->i; }", isSmartPointerLikeGetMethodCall())); + EXPECT_TRUE(matches( + Decls, "HasGetAndValue& Helper(); int target() { auto S = Helper(); }", + isSmartPointerLikeConstructor())); } TEST(SmartPointerAccessorCachingTest, Renamed) { @@ -390,6 +457,11 @@ TEST(SmartPointerAccessorCachingTest, Renamed) { EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias P) { return P->i; }", isPointerLikeOperatorArrow())); + + EXPECT_TRUE(matches( + Decls, + "std::unique_ptr& Helper(); int target() { auto S = Helper(); }", + isPointerLikeConstructor())); } } // namespace