Author: Florian Mayer Date: 2025-03-21T13:27:11-04:00 New Revision: 1cbcaa458020d7fa2b9b5d91db609f5a81d59346
URL: https://github.com/llvm/llvm-project/commit/1cbcaa458020d7fa2b9b5d91db609f5a81d59346 DIFF: https://github.com/llvm/llvm-project/commit/1cbcaa458020d7fa2b9b5d91db609f5a81d59346.diff LOG: [clang][dataflow] Add matcher for pointer-like types to be cached (#132314) This is used e.g. for iterators. Added: Modified: clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h b/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h index 1b116a0cf76ed..b4291347e0969 100644 --- a/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h +++ b/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h @@ -58,7 +58,9 @@ 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 isPointerLikeOperatorStar(); ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar(); +ast_matchers::StatementMatcher isPointerLikeOperatorArrow(); ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow(); ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall(); ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall(); diff --git a/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp b/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp index 4f5d18225c414..0860cc1dbaf8e 100644 --- a/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp +++ b/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp @@ -120,6 +120,14 @@ AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) { return HasStarAndArrow && (HasGet || HasValue); } +AST_MATCHER(clang::CXXRecordDecl, pointerClass) { + bool HasGet = false; + bool HasValue = false; + bool HasStarAndArrow = + clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue); + return HasStarAndArrow; +} + } // namespace namespace clang::dataflow { @@ -140,6 +148,22 @@ ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() { ofClass(smartPointerClassWithGetOrValue())))); } +ast_matchers::StatementMatcher isPointerLikeOperatorStar() { + return cxxOperatorCallExpr( + hasOverloadedOperatorName("*"), + callee(cxxMethodDecl(parameterCountIs(0), + returns(hasCanonicalType(referenceType())), + ofClass(pointerClass())))); +} + +ast_matchers::StatementMatcher isPointerLikeOperatorArrow() { + return cxxOperatorCallExpr( + hasOverloadedOperatorName("->"), + callee(cxxMethodDecl(parameterCountIs(0), + returns(hasCanonicalType(pointerType())), + ofClass(pointerClass())))); +} + ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall() { return cxxMemberCallExpr(callee(cxxMethodDecl( parameterCountIs(0), returns(hasCanonicalType(referenceType())), diff --git a/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp b/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp index 18b9f80e32bbf..0f7477d875960 100644 --- a/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp @@ -45,15 +45,62 @@ TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrowGet) { EXPECT_TRUE(matches(Decls, "int target(std::unique_ptr<S> P) { return (*P).i; }", isSmartPointerLikeOperatorStar())); + EXPECT_TRUE(matches(Decls, + "int target(std::unique_ptr<S> P) { return (*P).i; }", + isPointerLikeOperatorStar())); + EXPECT_TRUE(matches(Decls, "int target(std::unique_ptr<S> P) { return P->i; }", isSmartPointerLikeOperatorArrow())); + EXPECT_TRUE(matches(Decls, + "int target(std::unique_ptr<S> P) { return P->i; }", + isPointerLikeOperatorArrow())); + EXPECT_TRUE(matches(Decls, "int target(std::unique_ptr<S> P) { return P.get()->i; }", isSmartPointerLikeGetMethodCall())); EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }", isSmartPointerLikeOperatorArrow())); + EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }", + isPointerLikeOperatorArrow())); +} + +TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrow) { + llvm::StringRef Decls(R"cc( + namespace std { + template <class T> + struct unique_ptr { + T* operator->() const; + T& operator*() const; + }; + } // namespace std + + template <class T> + using UniquePtrAlias = std::unique_ptr<T>; + + struct S { int i; }; + )cc"); + + EXPECT_FALSE(matches(Decls, + "int target(std::unique_ptr<S> P) { return (*P).i; }", + isSmartPointerLikeOperatorStar())); + EXPECT_TRUE(matches(Decls, + "int target(std::unique_ptr<S> P) { return (*P).i; }", + isPointerLikeOperatorStar())); + + EXPECT_FALSE(matches(Decls, + "int target(std::unique_ptr<S> P) { return P->i; }", + isSmartPointerLikeOperatorArrow())); + EXPECT_TRUE(matches(Decls, + "int target(std::unique_ptr<S> P) { return P->i; }", + isPointerLikeOperatorArrow())); + + EXPECT_FALSE(matches(Decls, + "int target(UniquePtrAlias<S> P) { return P->i; }", + isSmartPointerLikeOperatorArrow())); + EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }", + isPointerLikeOperatorArrow())); } TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) { @@ -75,15 +122,25 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) { EXPECT_FALSE(matches(Decls, "int target(std::unique_ptr<S, T> P) { return (*P).i; }", isSmartPointerLikeOperatorStar())); + EXPECT_FALSE(matches(Decls, + "int target(std::unique_ptr<S, T> P) { return (*P).i; }", + isPointerLikeOperatorStar())); + EXPECT_FALSE(matches(Decls, "int target(std::unique_ptr<S, T> P) { return P->j; }", isSmartPointerLikeOperatorArrow())); + EXPECT_FALSE(matches(Decls, + "int target(std::unique_ptr<S, T> P) { return P->j; }", + isPointerLikeOperatorArrow())); // The class matching arguably accidentally matches, just because the // instantiation is with S, S. Hopefully doesn't happen too much in real code // with such operator* and operator-> overloads. EXPECT_TRUE(matches(Decls, "int target(std::unique_ptr<S, S> P) { return P->i; }", isSmartPointerLikeOperatorArrow())); + EXPECT_TRUE(matches(Decls, + "int target(std::unique_ptr<S, S> P) { return P->i; }", + isPointerLikeOperatorArrow())); } TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) { @@ -103,6 +160,9 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) { EXPECT_FALSE( matches(Decls, "int target(std::unique_ptr<S> P) { return (P * 10).i; }", isSmartPointerLikeOperatorStar())); + EXPECT_FALSE( + matches(Decls, "int target(std::unique_ptr<S> P) { return (P * 10).i; }", + isPointerLikeOperatorStar())); } TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) { @@ -122,9 +182,17 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) { EXPECT_FALSE(matches(Decls, "int target(std::unique_ptr<S> P) { return (*P).i; }", isSmartPointerLikeOperatorStar())); + EXPECT_FALSE(matches(Decls, + "int target(std::unique_ptr<S> P) { return (*P).i; }", + isPointerLikeOperatorStar())); + EXPECT_FALSE(matches(Decls, "int target(std::unique_ptr<S> P) { return P->i; }", isSmartPointerLikeOperatorArrow())); + EXPECT_FALSE(matches(Decls, + "int target(std::unique_ptr<S> P) { return P->i; }", + isPointerLikeOperatorArrow())); + EXPECT_FALSE( matches(Decls, "int target(std::unique_ptr<S> P) { return P.get()->i; }", isSmartPointerLikeGetMethodCall())); @@ -146,6 +214,10 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoStarMethod) { EXPECT_FALSE(matches(Decls, "int target(std::unique_ptr<S> P) { return P->i; }", isSmartPointerLikeOperatorArrow())); + EXPECT_FALSE(matches(Decls, + "int target(std::unique_ptr<S> P) { return P->i; }", + isPointerLikeOperatorArrow())); + EXPECT_FALSE(matches(Decls, "int target(std::unique_ptr<S> P) { return P->i; }", isSmartPointerLikeGetMethodCall())); @@ -171,15 +243,31 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) { EXPECT_TRUE(matches( Decls, "int target(std::optional<S> &NonConst) { return (*NonConst).i; }", isSmartPointerLikeOperatorStar())); + EXPECT_TRUE(matches( + Decls, "int target(std::optional<S> &NonConst) { return (*NonConst).i; }", + isPointerLikeOperatorStar())); + EXPECT_TRUE(matches( Decls, "int target(const std::optional<S> &Const) { return (*Const).i; }", isSmartPointerLikeOperatorStar())); + EXPECT_TRUE(matches( + Decls, "int target(const std::optional<S> &Const) { return (*Const).i; }", + isPointerLikeOperatorStar())); + EXPECT_TRUE(matches( Decls, "int target(std::optional<S> &NonConst) { return NonConst->i; }", isSmartPointerLikeOperatorArrow())); + EXPECT_TRUE(matches( + Decls, "int target(std::optional<S> &NonConst) { return NonConst->i; }", + isPointerLikeOperatorArrow())); + EXPECT_TRUE(matches( Decls, "int target(const std::optional<S> &Const) { return Const->i; }", isSmartPointerLikeOperatorArrow())); + EXPECT_TRUE(matches( + Decls, "int target(const std::optional<S> &Const) { return Const->i; }", + isPointerLikeOperatorArrow())); + EXPECT_TRUE(matches( Decls, "int target(std::optional<S> &NonConst) { return NonConst.value().i; }", @@ -214,16 +302,34 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) { Decls, "int target(HasGetAndValue<S> &NonConst) { return (*NonConst).i; }", isSmartPointerLikeOperatorStar())); + EXPECT_TRUE(matches( + Decls, + "int target(HasGetAndValue<S> &NonConst) { return (*NonConst).i; }", + isPointerLikeOperatorStar())); + EXPECT_TRUE(matches( Decls, "int target(const HasGetAndValue<S> &Const) { return (*Const).i; }", isSmartPointerLikeOperatorStar())); + EXPECT_TRUE(matches( + Decls, + "int target(const HasGetAndValue<S> &Const) { return (*Const).i; }", + isPointerLikeOperatorStar())); + EXPECT_TRUE(matches( Decls, "int target(HasGetAndValue<S> &NonConst) { return NonConst->i; }", isSmartPointerLikeOperatorArrow())); + EXPECT_TRUE(matches( + Decls, "int target(HasGetAndValue<S> &NonConst) { return NonConst->i; }", + isPointerLikeOperatorArrow())); + EXPECT_TRUE(matches( Decls, "int target(const HasGetAndValue<S> &Const) { return Const->i; }", isSmartPointerLikeOperatorArrow())); + EXPECT_TRUE(matches( + Decls, "int target(const HasGetAndValue<S> &Const) { return Const->i; }", + isPointerLikeOperatorArrow())); + EXPECT_TRUE(matches( Decls, "int target(HasGetAndValue<S> &NonConst) { return NonConst.value().i; }", _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits