Skip to content

Conversation

fmayer
Copy link
Contributor

@fmayer fmayer commented Oct 17, 2025

This is needed to handle circumstances like this in the StatusOr model:

const auto sor1 = Make<SmartPtrThing&>();
const auto sor2 = Make<SmartPtrThing&>();
if (!sor1->ok() && !sor2->ok()) return;
if (sor1->ok() && !sor2->ok()) {
} else if (!sor1->ok() && sor2->ok()) {
} else {
  sor1->value();
  sor2->value();
}

Created using spr 1.3.7
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html clang:analysis labels Oct 17, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 17, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-analysis

Author: Florian Mayer (fmayer)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/164031.diff

3 Files Affected:

  • (modified) clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h (+16)
  • (modified) clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp (+25-8)
  • (modified) clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp (+72)
diff --git a/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h b/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h
index b5bdff2df8ed6..c775d04f0ba8a 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,18 @@ void transferSmartPointerLikeCachedDeref(
   State.Env.setStorageLocation(*DerefExpr, LocForValue);
 }
 
+template <typename LatticeT>
+void transferSmartPointerLikeConstructor(
+    const CXXConstructExpr *ConstructOperator,
+    RecordStorageLocation *SmartPointerLoc, TransferState<LatticeT> &State,
+    llvm::function_ref<void(QualType, StorageLocation &)> InitializeLoc) {
+  const FunctionDecl *CanonicalCallee =
+      getCanonicalSmartPointerLikeOperatorCalleeForType(
+          ConstructOperator->getType()->getAsCXXRecordDecl());
+  State.Lattice.getOrCreateConstMethodReturnStorageLocation(
+      *SmartPointerLoc, CanonicalCallee, State.Env, InitializeLoc);
+}
+
 template <typename LatticeT>
 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<CXXMethodDecl>(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<CXXMethodDecl>(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<S> P) { return P->i; }",
                       isPointerLikeOperatorArrow()));
+
+  EXPECT_TRUE(matches(
+      Decls,
+      "std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
+      isSmartPointerLikeConstructor()));
+  EXPECT_TRUE(matches(
+      Decls,
+      "std::unique_ptr<S>& 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<S> P) { return P->i; }",
                       isPointerLikeOperatorArrow()));
+
+  EXPECT_FALSE(matches(
+      Decls,
+      "std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
+      isSmartPointerLikeConstructor()));
+  EXPECT_TRUE(matches(
+      Decls,
+      "std::unique_ptr<S>& 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<S, S> P) { return P->i; }",
                       isPointerLikeOperatorArrow()));
+
+  EXPECT_FALSE(matches(
+      Decls,
+      "std::unique_ptr<S, T>& Helper(); int target() { auto S = Helper(); }",
+      isSmartPointerLikeConstructor()));
+  EXPECT_FALSE(matches(
+      Decls,
+      "std::unique_ptr<S, T>& 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<S> P) { return (P * 10).i; }",
               isPointerLikeOperatorStar()));
+
+  EXPECT_FALSE(matches(
+      Decls,
+      "std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
+      isSmartPointerLikeConstructor()));
+  EXPECT_FALSE(matches(
+      Decls,
+      "std::unique_ptr<S>& 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<S> P) { return P.get()->i; }",
               isSmartPointerLikeGetMethodCall()));
+
+  EXPECT_FALSE(matches(
+      Decls,
+      "std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
+      isSmartPointerLikeConstructor()));
+  EXPECT_FALSE(matches(
+      Decls,
+      "std::unique_ptr<S>& 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<S> P) { return P->i; }",
                        isSmartPointerLikeGetMethodCall()));
+
+  EXPECT_FALSE(matches(
+      Decls,
+      "std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
+      isSmartPointerLikeConstructor()));
+  EXPECT_FALSE(matches(
+      Decls,
+      "std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
+      isPointerLikeConstructor()));
 }
 
 TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) {
@@ -276,6 +330,13 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) {
       Decls,
       "int target(const std::optional<S> &Const) { return Const.value().i; }",
       isSmartPointerLikeValueMethodCall()));
+
+  EXPECT_TRUE(matches(
+      Decls, "std::optional<S>& Helper(); int target() { auto S = Helper(); }",
+      isSmartPointerLikeConstructor()));
+  EXPECT_TRUE(matches(
+      Decls, "std::optional<S>& 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<S> &Const) { return Const->i; }",
       isPointerLikeOperatorArrow()));
+  EXPECT_TRUE(matches(
+      Decls, "HasGetAndValue<S>& Helper(); int target() { auto S = Helper(); }",
+      isPointerLikeConstructor()));
 
   EXPECT_TRUE(matches(
       Decls,
@@ -346,6 +410,9 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) {
       Decls,
       "int target(const HasGetAndValue<S> &Const) { return Const.get()->i; }",
       isSmartPointerLikeGetMethodCall()));
+  EXPECT_TRUE(matches(
+      Decls, "HasGetAndValue<S>& Helper(); int target() { auto S = Helper(); }",
+      isSmartPointerLikeConstructor()));
 }
 
 TEST(SmartPointerAccessorCachingTest, Renamed) {
@@ -390,6 +457,11 @@ TEST(SmartPointerAccessorCachingTest, Renamed) {
 
   EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
                       isPointerLikeOperatorArrow()));
+
+  EXPECT_TRUE(matches(
+      Decls,
+      "std::unique_ptr<S>& Helper(); int target() { auto S = Helper(); }",
+      isPointerLikeConstructor()));
 }
 
 } // namespace

@fmayer fmayer requested a review from jvoung October 17, 2025 23:15
Created using spr 1.3.7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:analysis clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants