Author: Florian Mayer
Date: 2025-03-28T17:51:30-07:00
New Revision: 842f25a5fb781491c14102d0db1886a78a190ed5

URL: 
https://github.com/llvm/llvm-project/commit/842f25a5fb781491c14102d0db1886a78a190ed5
DIFF: 
https://github.com/llvm/llvm-project/commit/842f25a5fb781491c14102d0db1886a78a190ed5.diff

LOG: [NFC] [dataflow] generalize smart pointer caching (#133350)

This allows us to use it for classes that use other names than get /
value.

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 b4291347e0969..e55b83aa845d4 100644
--- a/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h
+++ b/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h
@@ -62,8 +62,10 @@ ast_matchers::StatementMatcher isPointerLikeOperatorStar();
 ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar();
 ast_matchers::StatementMatcher isPointerLikeOperatorArrow();
 ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow();
-ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall();
-ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall();
+ast_matchers::StatementMatcher
+isSmartPointerLikeValueMethodCall(clang::StringRef MethodName = "value");
+ast_matchers::StatementMatcher
+isSmartPointerLikeGetMethodCall(clang::StringRef MethodName = "get");
 
 // Common transfer functions.
 

diff  --git a/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp 
b/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp
index 0860cc1dbaf8e..d87b2e6f03857 100644
--- a/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp
+++ b/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp
@@ -2,6 +2,7 @@
 
 #include "clang/AST/CanonicalType.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/Type.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/ASTMatchers/ASTMatchersMacros.h"
 #include "clang/Basic/OperatorKinds.h"
@@ -23,13 +24,28 @@ using ast_matchers::pointerType;
 using ast_matchers::referenceType;
 using ast_matchers::returns;
 
-bool hasSmartPointerClassShape(const CXXRecordDecl &RD, bool &HasGet,
-                               bool &HasValue) {
+CanQualType getLikeReturnType(QualType RT) {
+  if (!RT.isNull() && RT->isPointerType()) {
+    return RT->getPointeeType()
+        ->getCanonicalTypeUnqualified()
+        .getUnqualifiedType();
+  }
+  return {};
+}
+
+CanQualType valueLikeReturnType(QualType RT) {
+  if (!RT.isNull() && RT->isReferenceType()) {
+    return RT.getNonReferenceType()
+        ->getCanonicalTypeUnqualified()
+        .getUnqualifiedType();
+  }
+  return {};
+}
+
+CanQualType pointerLikeReturnType(const CXXRecordDecl &RD) {
   // We may want to cache this search, but in current profiles it hasn't shown
   // up as a hot spot (possibly because there aren't many hits, relatively).
-  bool HasArrow = false;
-  bool HasStar = false;
-  CanQualType StarReturnType, ArrowReturnType, GetReturnType, ValueReturnType;
+  CanQualType StarReturnType, ArrowReturnType;
   for (const auto *MD : RD.methods()) {
     // We only consider methods that are const and have zero parameters.
     // It may be that there is a non-const overload for the method, but
@@ -38,55 +54,35 @@ bool hasSmartPointerClassShape(const CXXRecordDecl &RD, 
bool &HasGet,
       continue;
     switch (MD->getOverloadedOperator()) {
     case OO_Star:
-      if (MD->getReturnType()->isReferenceType()) {
-        HasStar = true;
-        StarReturnType = MD->getReturnType()
-                             .getNonReferenceType()
-                             ->getCanonicalTypeUnqualified()
-                             .getUnqualifiedType();
-      }
+      StarReturnType = valueLikeReturnType(MD->getReturnType());
       break;
     case OO_Arrow:
-      if (MD->getReturnType()->isPointerType()) {
-        HasArrow = true;
-        ArrowReturnType = MD->getReturnType()
-                              ->getPointeeType()
-                              ->getCanonicalTypeUnqualified()
-                              .getUnqualifiedType();
-      }
+      ArrowReturnType = getLikeReturnType(MD->getReturnType());
       break;
-    case OO_None: {
-      IdentifierInfo *II = MD->getIdentifier();
-      if (II == nullptr)
-        continue;
-      if (II->isStr("get")) {
-        if (MD->getReturnType()->isPointerType()) {
-          HasGet = true;
-          GetReturnType = MD->getReturnType()
-                              ->getPointeeType()
-                              ->getCanonicalTypeUnqualified()
-                              .getUnqualifiedType();
-        }
-      } else if (II->isStr("value")) {
-        if (MD->getReturnType()->isReferenceType()) {
-          HasValue = true;
-          ValueReturnType = MD->getReturnType()
-                                .getNonReferenceType()
-                                ->getCanonicalTypeUnqualified()
-                                .getUnqualifiedType();
-        }
-      }
-    } break;
     default:
       break;
     }
   }
+  if (!StarReturnType.isNull() && !ArrowReturnType.isNull() &&
+      StarReturnType == ArrowReturnType)
+    return StarReturnType;
 
-  if (!HasStar || !HasArrow || StarReturnType != ArrowReturnType)
-    return false;
-  HasGet = HasGet && (GetReturnType == StarReturnType);
-  HasValue = HasValue && (ValueReturnType == StarReturnType);
-  return true;
+  return {};
+}
+
+QualType findReturnType(const CXXRecordDecl &RD, StringRef MethodName) {
+  for (const auto *MD : RD.methods()) {
+    // We only consider methods that are const and have zero parameters.
+    // It may be that there is a non-const overload for the method, but
+    // there should at least be a const overload as well.
+    if (!MD->isConst() || MD->getNumParams() != 0 ||
+        MD->getOverloadedOperator() != OO_None)
+      continue;
+    clang::IdentifierInfo *II = MD->getIdentifier();
+    if (II && II->isStr(MethodName))
+      return MD->getReturnType();
+  }
+  return {};
 }
 
 } // namespace
@@ -96,36 +92,37 @@ bool hasSmartPointerClassShape(const CXXRecordDecl &RD, 
bool &HasGet,
 // its own anonymous namespace instead of in clang::dataflow.
 namespace {
 
-AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGet) {
-  bool HasGet = false;
-  bool HasValue = false;
-  bool HasStarAndArrow =
-      clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
-  return HasStarAndArrow && HasGet;
+using clang::dataflow::findReturnType;
+using clang::dataflow::getLikeReturnType;
+using clang::dataflow::pointerLikeReturnType;
+using clang::dataflow::valueLikeReturnType;
+
+AST_MATCHER_P(clang::CXXRecordDecl, smartPointerClassWithGetLike,
+              clang::StringRef, MethodName) {
+  auto RT = pointerLikeReturnType(Node);
+  if (RT.isNull())
+    return false;
+  return getLikeReturnType(findReturnType(Node, MethodName)) == RT;
 }
 
-AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithValue) {
-  bool HasGet = false;
-  bool HasValue = false;
-  bool HasStarAndArrow =
-      clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
-  return HasStarAndArrow && HasValue;
+AST_MATCHER_P(clang::CXXRecordDecl, smartPointerClassWithValueLike,
+              clang::StringRef, MethodName) {
+  auto RT = pointerLikeReturnType(Node);
+  if (RT.isNull())
+    return false;
+  return valueLikeReturnType(findReturnType(Node, MethodName)) == RT;
 }
 
 AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) {
-  bool HasGet = false;
-  bool HasValue = false;
-  bool HasStarAndArrow =
-      clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
-  return HasStarAndArrow && (HasGet || HasValue);
+  auto RT = pointerLikeReturnType(Node);
+  if (RT.isNull())
+    return false;
+  return getLikeReturnType(findReturnType(Node, "get")) == RT ||
+         valueLikeReturnType(findReturnType(Node, "value")) == RT;
 }
 
 AST_MATCHER(clang::CXXRecordDecl, pointerClass) {
-  bool HasGet = false;
-  bool HasValue = false;
-  bool HasStarAndArrow =
-      clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
-  return HasStarAndArrow;
+  return !pointerLikeReturnType(Node).isNull();
 }
 
 } // namespace
@@ -164,16 +161,19 @@ ast_matchers::StatementMatcher 
isPointerLikeOperatorArrow() {
                            ofClass(pointerClass()))));
 }
 
-ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall() {
+ast_matchers::StatementMatcher
+isSmartPointerLikeValueMethodCall(clang::StringRef MethodName) {
   return cxxMemberCallExpr(callee(cxxMethodDecl(
       parameterCountIs(0), returns(hasCanonicalType(referenceType())),
-      hasName("value"), ofClass(smartPointerClassWithValue()))));
+      hasName(MethodName),
+      ofClass(smartPointerClassWithValueLike(MethodName)))));
 }
 
-ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall() {
+ast_matchers::StatementMatcher
+isSmartPointerLikeGetMethodCall(clang::StringRef MethodName) {
   return cxxMemberCallExpr(callee(cxxMethodDecl(
       parameterCountIs(0), returns(hasCanonicalType(pointerType())),
-      hasName("get"), ofClass(smartPointerClassWithGet()))));
+      hasName(MethodName), 
ofClass(smartPointerClassWithGetLike(MethodName)))));
 }
 
 const FunctionDecl *

diff  --git 
a/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp 
b/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp
index 0f7477d875960..5d3d5b0cfea09 100644
--- a/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp
@@ -348,5 +348,49 @@ TEST(SmartPointerAccessorCachingTest, 
MatchesWithTypeAliases) {
       isSmartPointerLikeGetMethodCall()));
 }
 
+TEST(SmartPointerAccessorCachingTest, Renamed) {
+  llvm::StringRef Decls(R"cc(
+    namespace std {
+    template <class T>
+    struct unique_ptr {
+      T* operator->() const;
+      T& operator*() const;
+      T* Get() const;
+      T& Value() const;
+    };
+    }  // namespace std
+
+    template <class T>
+    using UniquePtrAlias = std::unique_ptr<T>;
+
+    struct S { int i; };
+  )cc");
+
+  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; }",
+                      isPointerLikeOperatorArrow()));
+
+  EXPECT_TRUE(matches(Decls,
+                      "int target(std::unique_ptr<S> P) { return P.Get()->i; 
}",
+                      isSmartPointerLikeGetMethodCall("Get")));
+  EXPECT_TRUE(matches(Decls,
+                      "int target(UniquePtrAlias<S> P) { return P.Get()->i; }",
+                      isSmartPointerLikeGetMethodCall("Get")));
+
+  EXPECT_TRUE(
+      matches(Decls, "int target(std::unique_ptr<S> P) { return P.Value().i; 
}",
+              isSmartPointerLikeValueMethodCall("Value")));
+  EXPECT_TRUE(matches(Decls,
+                      "int target(UniquePtrAlias<S> P) { return P.Value().i; 
}",
+                      isSmartPointerLikeValueMethodCall("Value")));
+
+  EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; 
}",
+                      isPointerLikeOperatorArrow()));
+}
+
 } // namespace
 } // namespace clang::dataflow


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to