[clang] [clang][dataflow] Add matchers for smart pointer accessors to be cached (PR #120102)

Gábor Horváth via cfe-commits cfe-commits at lists.llvm.org
Tue Dec 17 04:03:30 PST 2024


================
@@ -0,0 +1,134 @@
+#include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h"
+
+#include "clang/AST/CanonicalType.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/OperatorKinds.h"
+
+namespace clang::dataflow {
+
+namespace {
+
+using ast_matchers::callee;
+using ast_matchers::cxxMemberCallExpr;
+using ast_matchers::cxxMethodDecl;
+using ast_matchers::cxxOperatorCallExpr;
+using ast_matchers::hasName;
+using ast_matchers::hasOverloadedOperatorName;
+using ast_matchers::ofClass;
+using ast_matchers::parameterCountIs;
+using ast_matchers::pointerType;
+using ast_matchers::referenceType;
+using ast_matchers::returns;
+
+bool hasSmartPointerClassShape(const CXXRecordDecl &RD, bool &HasGet,
+                               bool &HasValue) {
+  // 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;
+  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) {
+      continue;
+    }
+    if (MD->getOverloadedOperator() == OO_Star &&
+        MD->getReturnType()->isReferenceType()) {
+      HasStar = true;
+      StarReturnType = MD->getReturnType()
+                           .getNonReferenceType()
+                           ->getCanonicalTypeUnqualified();
+    } else if (MD->getOverloadedOperator() == OO_Arrow &&
+               MD->getReturnType()->isPointerType()) {
+      HasArrow = true;
+      ArrowReturnType =
+          MD->getReturnType()->getPointeeType()->getCanonicalTypeUnqualified();
+    } else {
+      IdentifierInfo *II = MD->getIdentifier();
+      if (II == nullptr)
+        continue;
+      if (II->isStr("get") && MD->getReturnType()->isPointerType()) {
+        HasGet = true;
+        GetReturnType = MD->getReturnType()
+                            ->getPointeeType()
+                            ->getCanonicalTypeUnqualified();
+      } else if (II->isStr("value") && MD->getReturnType()->isReferenceType()) {
+        HasValue = true;
+        ValueReturnType = MD->getReturnType()
+                              .getNonReferenceType()
+                              ->getCanonicalTypeUnqualified();
+      }
+    }
+  }
+
+  if (!HasStar || !HasArrow || StarReturnType != ArrowReturnType)
+    return false;
+  HasGet = HasGet && (GetReturnType == StarReturnType);
+  HasValue = HasValue && (ValueReturnType == StarReturnType);
+  return true;
+}
+
+} // namespace
+} // namespace clang::dataflow
+
+// AST_MATCHER macros create an "internal" namespace, so we put it in
+// 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;
+}
+
+AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithValue) {
+  bool HasGet = false;
+  bool HasValue = false;
+  bool HasStarAndArrow =
+      clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
+  return HasStarAndArrow && HasValue;
+}
+
+AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) {
+  bool HasGet = false;
+  bool HasValue = false;
+  bool HasStarAndArrow =
+      clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
+  return HasStarAndArrow && (HasGet || HasValue);
+}
+
+} // namespace
+
+namespace clang::dataflow {
+
+ast_matchers::internal::Matcher<Stmt> isSmartPointerLikeOperatorStar() {
----------------
Xazax-hun wrote:

I am not sure how these matchers would be used but I wonder if we end up doing a lot of duplicated work using this paradigm. Alternatively, we could just match the smart pointer class once in one matcher, and bound all points of interests to certain names. The user can later on use these names to check if there was a match for the operators we are interested in or not. 

https://github.com/llvm/llvm-project/pull/120102


More information about the cfe-commits mailing list