[clang] 1cbcaa4 - [clang][dataflow] Add matcher for pointer-like types to be cached (#132314)

via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 21 10:27:13 PDT 2025


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; }",


        


More information about the cfe-commits mailing list