https://github.com/balazske updated 
https://github.com/llvm/llvm-project/pull/164600

From 3d117776d39d65aec50c24b2bff18bddf0570037 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <[email protected]>
Date: Wed, 22 Oct 2025 09:41:01 +0200
Subject: [PATCH 1/3] [clang][analyzer] CallAndMessage warnings at pointer to
 uninitialized struct

CallAndMessageChecker did have a warning for the case when pointer to
uninitialized data is passed to a function when the argument type is
pointer to const. This did not work for struct types. The check is
improved to handle cases when struct with uninitialized data is
passed to a function.
---
 .../Checkers/CallAndMessageChecker.cpp               | 12 ++++++++----
 clang/test/Analysis/PR40625.cpp                      |  4 ++--
 clang/test/Analysis/call-and-message.c               | 12 ++++++++++++
 3 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index 7cc146ed29d0d..cd24b1e816e01 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -183,8 +183,6 @@ bool CallAndMessageChecker::uninitRefOrPointer(
     CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
     const BugType &BT, const ParmVarDecl *ParamDecl, int ArgumentNumber) const 
{
 
-  // The pointee being uninitialized is a sign of code smell, not a bug, no 
need
-  // to sink here.
   if (!ChecksEnabled[CK_ArgPointeeInitializedness])
     return false;
 
@@ -212,8 +210,14 @@ bool CallAndMessageChecker::uninitRefOrPointer(
 
   if (const MemRegion *SValMemRegion = V.getAsRegion()) {
     const ProgramStateRef State = C.getState();
-    const SVal PSV = State->getSVal(SValMemRegion, C.getASTContext().CharTy);
-    if (PSV.isUndef()) {
+    QualType T = ParamDecl->getType()->getPointeeType();
+    if (T->isVoidType())
+      T = C.getASTContext().CharTy;
+    const SVal PSV = State->getSVal(SValMemRegion, T);
+    bool IsUndef = PSV.isUndef();
+    if (auto LCV = PSV.getAs<nonloc::LazyCompoundVal>())
+      IsUndef = LCV->getStore() == nullptr;
+    if (IsUndef) {
       if (ExplodedNode *N = C.generateErrorNode()) {
         auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
         R->addRange(ArgRange);
diff --git a/clang/test/Analysis/PR40625.cpp b/clang/test/Analysis/PR40625.cpp
index 5ebe2122945e6..ab3faa328298a 100644
--- a/clang/test/Analysis/PR40625.cpp
+++ b/clang/test/Analysis/PR40625.cpp
@@ -5,11 +5,11 @@
 void f(const int *end);
 
 void g(const int (&arrr)[10]) {
-  f(arrr); // expected-warning{{1st function call argument is a pointer to 
uninitialized value}}
+  f(arrr);
 }
 
 void h() {
   int arr[10];
 
-  g(arr);
+  g(arr); // expected-warning{{1st function call argument is an uninitialized 
value}}
 }
diff --git a/clang/test/Analysis/call-and-message.c 
b/clang/test/Analysis/call-and-message.c
index ade51145e2a93..fdac77176569b 100644
--- a/clang/test/Analysis/call-and-message.c
+++ b/clang/test/Analysis/call-and-message.c
@@ -24,6 +24,18 @@ void pointee_uninit(void) {
   doStuff_pointerToConstInt(p); // expected-warning{{1st function call 
argument is a pointer to uninitialized value [core.CallAndMessage]}}
 }
 
+typedef struct S {
+  int a;
+  short b;
+} S;
+
+void doStuff_pointerToConstStruct(const S *s){};
+void pointee_uninit_struct(void) {
+  S s;
+  S *p = &s;
+  doStuff_pointerToConstStruct(p); // expected-warning{{1st function call 
argument is a pointer to uninitialized value [core.CallAndMessage]}}
+}
+
 // TODO: If this hash ever changes, turn
 // core.CallAndMessage:ArgPointeeInitializedness from a checker option into a
 // checker, as described in the CallAndMessage comments!

From 2ec7140834e9d76efe128921749fa8e9d0049f54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <[email protected]>
Date: Fri, 31 Oct 2025 17:46:56 +0100
Subject: [PATCH 2/3] use the code already included in the checker to find
 uninitialized LCV

---
 .../Checkers/CallAndMessageChecker.cpp        | 176 +++++++++++-------
 clang/test/Analysis/PR40625.cpp               |   4 +-
 clang/test/Analysis/call-and-message-cpp.cpp  |  78 ++++++++
 clang/test/Analysis/call-and-message.c        |  18 +-
 4 files changed, 204 insertions(+), 72 deletions(-)
 create mode 100644 clang/test/Analysis/call-and-message-cpp.cpp

diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index cd24b1e816e01..cc603f547ecef 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -179,59 +179,6 @@ static void describeUninitializedArgumentInCall(const 
CallEvent &Call,
   }
 }
 
-bool CallAndMessageChecker::uninitRefOrPointer(
-    CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
-    const BugType &BT, const ParmVarDecl *ParamDecl, int ArgumentNumber) const 
{
-
-  if (!ChecksEnabled[CK_ArgPointeeInitializedness])
-    return false;
-
-  // No parameter declaration available, i.e. variadic function argument.
-  if(!ParamDecl)
-    return false;
-
-  // If parameter is declared as pointer to const in function declaration,
-  // then check if corresponding argument in function call is
-  // pointing to undefined symbol value (uninitialized memory).
-  SmallString<200> Buf;
-  llvm::raw_svector_ostream Os(Buf);
-
-  if (ParamDecl->getType()->isPointerType()) {
-    Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
-       << " function call argument is a pointer to uninitialized value";
-  } else if (ParamDecl->getType()->isReferenceType()) {
-    Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
-       << " function call argument is an uninitialized value";
-  } else
-    return false;
-
-  if(!ParamDecl->getType()->getPointeeType().isConstQualified())
-    return false;
-
-  if (const MemRegion *SValMemRegion = V.getAsRegion()) {
-    const ProgramStateRef State = C.getState();
-    QualType T = ParamDecl->getType()->getPointeeType();
-    if (T->isVoidType())
-      T = C.getASTContext().CharTy;
-    const SVal PSV = State->getSVal(SValMemRegion, T);
-    bool IsUndef = PSV.isUndef();
-    if (auto LCV = PSV.getAs<nonloc::LazyCompoundVal>())
-      IsUndef = LCV->getStore() == nullptr;
-    if (IsUndef) {
-      if (ExplodedNode *N = C.generateErrorNode()) {
-        auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
-        R->addRange(ArgRange);
-        if (ArgEx)
-          bugreporter::trackExpressionValue(N, ArgEx, *R);
-
-        C.emitReport(std::move(R));
-      }
-      return true;
-    }
-  }
-  return false;
-}
-
 namespace {
 class FindUninitializedField {
 public:
@@ -272,9 +219,115 @@ class FindUninitializedField {
 
     return false;
   }
+
+  void printFieldChain(llvm::raw_ostream &OS) {
+    if (FieldChain.size() == 1)
+      OS << " (e.g., field: '" << *FieldChain[0] << "')";
+    else {
+      OS << " (e.g., via the field chain: '";
+      bool First = true;
+      for (SmallVectorImpl<const FieldDecl *>::iterator DI = 
FieldChain.begin(),
+                                                        DE = FieldChain.end();
+           DI != DE; ++DI) {
+        if (First)
+          First = false;
+        else
+          OS << '.';
+        OS << **DI;
+      }
+      OS << "')";
+    }
+  }
 };
 } // namespace
 
+bool CallAndMessageChecker::uninitRefOrPointer(
+    CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
+    const BugType &BT, const ParmVarDecl *ParamDecl, int ArgumentNumber) const 
{
+
+  if (!ChecksEnabled[CK_ArgPointeeInitializedness])
+    return false;
+
+  // No parameter declaration available, i.e. variadic function argument.
+  if (!ParamDecl)
+    return false;
+
+  QualType ParamT = ParamDecl->getType();
+  if (!ParamT->isPointerOrReferenceType())
+    return false;
+
+  QualType PointeeT = ParamT->getPointeeType();
+  if (!PointeeT.isConstQualified())
+    return false;
+
+  const MemRegion *SValMemRegion = V.getAsRegion();
+  if (!SValMemRegion)
+    return false;
+
+  // If parameter is declared as pointer to const in function declaration,
+  // then check if corresponding argument in function call is
+  // pointing to undefined symbol value (uninitialized memory).
+
+  const ProgramStateRef State = C.getState();
+  if (PointeeT->isVoidType())
+    PointeeT = C.getASTContext().CharTy;
+  const SVal PointeeV =
+      State->getSVal(SValMemRegion, PointeeT);
+
+  if (PointeeV.isUndef()) {
+    if (ExplodedNode *N = C.generateErrorNode()) {
+      SmallString<200> Buf;
+      llvm::raw_svector_ostream Os(Buf);
+      Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
+         << " function call argument is ";
+      if (ParamT->isPointerType())
+        Os << "a pointer to uninitialized value";
+      else
+        Os << "an uninitialized value";
+      auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
+      R->addRange(ArgRange);
+      if (ArgEx)
+        bugreporter::trackExpressionValue(N, ArgEx, *R);
+
+      C.emitReport(std::move(R));
+    }
+    return true;
+  }
+
+  if (auto LV = PointeeV.getAs<nonloc::LazyCompoundVal>()) {
+    const LazyCompoundValData *D = LV->getCVData();
+    FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
+                             C.getSValBuilder().getRegionManager(),
+                             D->getStore());
+
+    if (F.Find(D->getRegion())) {
+      if (ExplodedNode *N = C.generateErrorNode()) {
+        SmallString<512> Buf;
+        llvm::raw_svector_ostream Os(Buf);
+        Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 
1)
+           << " function call argument";
+        if (ParamT->isPointerType())
+          Os << " points to";
+        else
+          Os << " references";
+        Os << " an uninitialized value";
+
+        F.printFieldChain(Os);
+
+        auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
+        R->addRange(ArgRange);
+
+        if (ArgEx)
+          bugreporter::trackExpressionValue(N, ArgEx, *R);
+        C.emitReport(std::move(R));
+      }
+      return true;
+    }
+  }
+
+  return false;
+}
+
 bool CallAndMessageChecker::PreVisitProcessArg(
     CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
     int ArgumentNumber, bool CheckUninitFields, const CallEvent &Call,
@@ -320,22 +373,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(
         SmallString<512> Str;
         llvm::raw_svector_ostream os(Str);
         os << "Passed-by-value struct argument contains uninitialized data";
-
-        if (F.FieldChain.size() == 1)
-          os << " (e.g., field: '" << *F.FieldChain[0] << "')";
-        else {
-          os << " (e.g., via the field chain: '";
-          bool first = true;
-          for (SmallVectorImpl<const FieldDecl *>::iterator
-               DI = F.FieldChain.begin(), DE = F.FieldChain.end(); 
DI!=DE;++DI){
-            if (first)
-              first = false;
-            else
-              os << '.';
-            os << **DI;
-          }
-          os << "')";
-        }
+        F.printFieldChain(os);
 
         // Generate a report for this bug.
         auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
diff --git a/clang/test/Analysis/PR40625.cpp b/clang/test/Analysis/PR40625.cpp
index ab3faa328298a..5ebe2122945e6 100644
--- a/clang/test/Analysis/PR40625.cpp
+++ b/clang/test/Analysis/PR40625.cpp
@@ -5,11 +5,11 @@
 void f(const int *end);
 
 void g(const int (&arrr)[10]) {
-  f(arrr);
+  f(arrr); // expected-warning{{1st function call argument is a pointer to 
uninitialized value}}
 }
 
 void h() {
   int arr[10];
 
-  g(arr); // expected-warning{{1st function call argument is an uninitialized 
value}}
+  g(arr);
 }
diff --git a/clang/test/Analysis/call-and-message-cpp.cpp 
b/clang/test/Analysis/call-and-message-cpp.cpp
new file mode 100644
index 0000000000000..b38705d6ac3a3
--- /dev/null
+++ b/clang/test/Analysis/call-and-message-cpp.cpp
@@ -0,0 +1,78 @@
+// RUN: %clang_analyze_cc1 %s -verify \
+// RUN:   -analyzer-checker=core \
+// RUN:   -analyzer-config core.CallAndMessage:ArgPointeeInitializedness=true \
+// RUN:   -analyzer-config core.CallAndMessage:ArgInitializedness=false
+
+struct S1 {
+  char c;
+};
+
+struct S {
+  int a;
+  S1 b;
+};
+
+S GlobalS;
+
+void doStuffP(const S *);
+void doStuffR(const S &);
+
+void uninit_val_p() {
+  S s;
+  doStuffP(&s); // expected-warning{{1st function call argument points to an 
uninitialized value (e.g., field: 'a')}}
+}
+
+void uninit_val_r() {
+  S s;
+  s.a = 0;
+  doStuffR(s); // expected-warning{{1st function call argument references an 
uninitialized value (e.g., via the field chain: 'b.c')}}
+}
+
+S *uninit_new() {
+  S *s = new S;
+  doStuffP(s); // expected-warning{{1st function call argument points to an 
uninitialized value (e.g., field: 'a')}}
+  return s;
+}
+
+void uninit_ctr() {
+  S s = S();
+  doStuffP(&s);
+}
+
+void uninit_init() {
+  S s{};
+  doStuffP(&s);
+}
+
+void uninit_init_val() {
+  S s{1, {2}};
+  doStuffP(&s);
+}
+
+void uninit_parm_ptr(S *s) {
+  doStuffP(s);
+}
+
+void uninit_parm_val(S s) {
+  doStuffP(&s);
+}
+
+void uninit_parm_ref(S &s) {
+  doStuffP(&s);
+}
+
+void init_val() {
+  S s;
+  s.a = 1;
+  s.b.c = 1;
+  doStuffP(&s);
+}
+
+void uninit_global() {
+  doStuffP(&GlobalS);
+}
+
+void uninit_static() {
+  static S s;
+  doStuffP(&s);
+}
diff --git a/clang/test/Analysis/call-and-message.c 
b/clang/test/Analysis/call-and-message.c
index fdac77176569b..5f34635123426 100644
--- a/clang/test/Analysis/call-and-message.c
+++ b/clang/test/Analysis/call-and-message.c
@@ -33,7 +33,23 @@ void doStuff_pointerToConstStruct(const S *s){};
 void pointee_uninit_struct(void) {
   S s;
   S *p = &s;
-  doStuff_pointerToConstStruct(p); // expected-warning{{1st function call 
argument is a pointer to uninitialized value [core.CallAndMessage]}}
+  doStuff_pointerToConstStruct(p); // expected-warning{{1st function call 
argument points to an uninitialized value (e.g., field: 'a') 
[core.CallAndMessage]}}
+}
+void pointee_uninit_struct_1(void) {
+  S s;
+  s.a = 2;
+  doStuff_pointerToConstStruct(&s); // expected-warning{{1st function call 
argument points to an uninitialized value (e.g., field: 'b') 
[core.CallAndMessage]}}
+}
+void pointee_uninit_struct_2(void) {
+  S s = {};
+  doStuff_pointerToConstStruct(&s);
+}
+void pointee_uninit_struct_3(S *s) {
+  doStuff_pointerToConstStruct(s);
+}
+void pointee_uninit_struct_4(void) {
+  S s = {1, 2};
+  doStuff_pointerToConstStruct(&s);
 }
 
 // TODO: If this hash ever changes, turn

From fc0c6a0a104f6cdf7ccacdc8548fc1118ad140db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bal=C3=A1zs=20K=C3=A9ri?= <[email protected]>
Date: Thu, 20 Nov 2025 16:11:49 +0100
Subject: [PATCH 3/3] using formatv

---
 .../Checkers/CallAndMessageChecker.cpp        | 72 +++++++++----------
 1 file changed, 33 insertions(+), 39 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index cc603f547ecef..2ebc48bfc38c7 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -21,6 +21,7 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 #include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/raw_ostream.h"
 
 using namespace clang;
@@ -182,7 +183,8 @@ static void describeUninitializedArgumentInCall(const 
CallEvent &Call,
 namespace {
 class FindUninitializedField {
 public:
-  SmallVector<const FieldDecl *, 10> FieldChain;
+  using FieldChainTy = SmallVector<const FieldDecl *, 10>;
+  FieldChainTy FieldChain;
 
 private:
   StoreManager &StoreMgr;
@@ -219,27 +221,32 @@ class FindUninitializedField {
 
     return false;
   }
+};
+} // namespace
 
-  void printFieldChain(llvm::raw_ostream &OS) {
-    if (FieldChain.size() == 1)
-      OS << " (e.g., field: '" << *FieldChain[0] << "')";
+namespace llvm {
+template <> struct format_provider<FindUninitializedField::FieldChainTy> {
+  static void format(const FindUninitializedField::FieldChainTy &V,
+                     raw_ostream &Stream, StringRef Style) {
+    if (V.size() == 1)
+      Stream << "(e.g., field: '" << *V[0] << "')";
     else {
-      OS << " (e.g., via the field chain: '";
+      Stream << "(e.g., via the field chain: '";
       bool First = true;
-      for (SmallVectorImpl<const FieldDecl *>::iterator DI = 
FieldChain.begin(),
-                                                        DE = FieldChain.end();
+      for (FindUninitializedField::FieldChainTy::const_iterator DI = V.begin(),
+                                                                DE = V.end();
            DI != DE; ++DI) {
         if (First)
           First = false;
         else
-          OS << '.';
-        OS << **DI;
+          Stream << '.';
+        Stream << **DI;
       }
-      OS << "')";
+      Stream << "')";
     }
   }
 };
-} // namespace
+} // namespace llvm
 
 bool CallAndMessageChecker::uninitRefOrPointer(
     CheckerContext &C, SVal V, SourceRange ArgRange, const Expr *ArgEx,
@@ -276,15 +283,11 @@ bool CallAndMessageChecker::uninitRefOrPointer(
 
   if (PointeeV.isUndef()) {
     if (ExplodedNode *N = C.generateErrorNode()) {
-      SmallString<200> Buf;
-      llvm::raw_svector_ostream Os(Buf);
-      Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
-         << " function call argument is ";
-      if (ParamT->isPointerType())
-        Os << "a pointer to uninitialized value";
-      else
-        Os << "an uninitialized value";
-      auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
+      std::string Msg = llvm::formatv(
+          "{0}{1} function call argument is {2} uninitialized value",
+          ArgumentNumber + 1, llvm::getOrdinalSuffix(ArgumentNumber + 1),
+          ParamT->isPointerType() ? "a pointer to" : "an");
+      auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
       R->addRange(ArgRange);
       if (ArgEx)
         bugreporter::trackExpressionValue(N, ArgEx, *R);
@@ -302,23 +305,15 @@ bool CallAndMessageChecker::uninitRefOrPointer(
 
     if (F.Find(D->getRegion())) {
       if (ExplodedNode *N = C.generateErrorNode()) {
-        SmallString<512> Buf;
-        llvm::raw_svector_ostream Os(Buf);
-        Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 
1)
-           << " function call argument";
-        if (ParamT->isPointerType())
-          Os << " points to";
-        else
-          Os << " references";
-        Os << " an uninitialized value";
-
-        F.printFieldChain(Os);
-
-        auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
+        std::string Msg = llvm::formatv(
+            "{0}{1} function call argument {2} an uninitialized value {3}",
+            (ArgumentNumber + 1), llvm::getOrdinalSuffix(ArgumentNumber + 1),
+            ParamT->isPointerType() ? "points to" : "references", 
F.FieldChain);
+        auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
         R->addRange(ArgRange);
-
         if (ArgEx)
           bugreporter::trackExpressionValue(N, ArgEx, *R);
+
         C.emitReport(std::move(R));
       }
       return true;
@@ -370,13 +365,12 @@ bool CallAndMessageChecker::PreVisitProcessArg(
         return true;
       }
       if (ExplodedNode *N = C.generateErrorNode()) {
-        SmallString<512> Str;
-        llvm::raw_svector_ostream os(Str);
-        os << "Passed-by-value struct argument contains uninitialized data";
-        F.printFieldChain(os);
+        std::string Msg = llvm::formatv(
+            "Passed-by-value struct argument contains uninitialized data {0}",
+            F.FieldChain);
 
         // Generate a report for this bug.
-        auto R = std::make_unique<PathSensitiveBugReport>(BT, os.str(), N);
+        auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
         R->addRange(ArgRange);
 
         if (ArgEx)

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to