[clang] [libcxx] [clang] Add -Wuninitialized-const-pointer (PR #148337)

Igor Kudrin via cfe-commits cfe-commits at lists.llvm.org
Mon Jul 14 14:49:53 PDT 2025


https://github.com/igorkudrin updated https://github.com/llvm/llvm-project/pull/148337

>From 8343c946e874ee27e71781ac1bd10fb680b08fbf Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Fri, 11 Jul 2025 18:23:10 -0700
Subject: [PATCH 1/2] [clang] Add -Wuninitialized-const-pointer

This option is similar to -Wuninitialized-const-reference, but diagnoses
the passing of an uninitialized value via a const pointer, like in the
following code:
```
void foo(const int *);
void test() {
  int v;
  foo(&v);
}
```
---
 .../Analysis/Analyses/UninitializedValues.h   |  6 ++++
 clang/include/clang/Basic/DiagnosticGroups.td |  4 ++-
 .../clang/Basic/DiagnosticSemaKinds.td        |  4 +++
 clang/lib/Analysis/UninitializedValues.cpp    | 19 ++++++++--
 clang/lib/Sema/AnalysisBasedWarnings.cpp      | 21 ++++++++---
 clang/test/Misc/warning-wall.c                |  1 +
 .../warn-uninitialized-const-pointer.cpp      | 35 +++++++++++++++++++
 .../pointer.pass.cpp                          |  4 +--
 .../pointer.volatile.pass.cpp                 |  4 +--
 9 files changed, 87 insertions(+), 11 deletions(-)
 create mode 100644 clang/test/SemaCXX/warn-uninitialized-const-pointer.cpp

diff --git a/clang/include/clang/Analysis/Analyses/UninitializedValues.h b/clang/include/clang/Analysis/Analyses/UninitializedValues.h
index b151bc3f58321..a9b9caf38e518 100644
--- a/clang/include/clang/Analysis/Analyses/UninitializedValues.h
+++ b/clang/include/clang/Analysis/Analyses/UninitializedValues.h
@@ -50,6 +50,9 @@ class UninitUse {
   /// Is this use a const reference to this variable?
   bool ConstRefUse = false;
 
+  /// Is this use a const pointer to this variable?
+  bool ConstPtrUse = false;
+
   /// This use is always uninitialized if it occurs after any of these branches
   /// is taken.
   SmallVector<Branch, 2> UninitBranches;
@@ -65,11 +68,14 @@ class UninitUse {
   void setUninitAfterCall() { UninitAfterCall = true; }
   void setUninitAfterDecl() { UninitAfterDecl = true; }
   void setConstRefUse() { ConstRefUse = true; }
+  void setConstPtrUse() { ConstPtrUse = true; }
 
   /// Get the expression containing the uninitialized use.
   const Expr *getUser() const { return User; }
 
   bool isConstRefUse() const { return ConstRefUse; }
+  bool isConstPtrUse() const { return ConstPtrUse; }
+  bool isConstRefOrPtrUse() const { return ConstRefUse || ConstPtrUse; }
 
   /// The kind of uninitialized use.
   enum Kind {
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 9a7a308600763..c28a919e35d08 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -952,9 +952,11 @@ def UninitializedMaybe : DiagGroup<"conditional-uninitialized">;
 def UninitializedSometimes : DiagGroup<"sometimes-uninitialized">;
 def UninitializedStaticSelfInit : DiagGroup<"static-self-init">;
 def UninitializedConstReference : DiagGroup<"uninitialized-const-reference">;
+def UninitializedConstPointer : DiagGroup<"uninitialized-const-pointer">;
 def Uninitialized  : DiagGroup<"uninitialized", [UninitializedSometimes,
                                                  UninitializedStaticSelfInit,
-                                                 UninitializedConstReference]>;
+                                                 UninitializedConstReference,
+                                                 UninitializedConstPointer]>;
 def IgnoredPragmaIntrinsic : DiagGroup<"ignored-pragma-intrinsic">;
 // #pragma optimize is often used to avoid to work around MSVC codegen bugs or
 // to disable inlining. It's not completely clear what alternative to suggest
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f1290738d46b2..42e351c3fd697 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2548,6 +2548,10 @@ def warn_uninit_const_reference : Warning<
   "variable %0 is uninitialized when passed as a const reference argument "
   "here">, InGroup<UninitializedConstReference>, DefaultIgnore;
 
+def warn_uninit_const_pointer : Warning<
+  "variable %0 is uninitialized when passed as a const pointer argument here">,
+  InGroup<UninitializedConstPointer>, DefaultIgnore;
+
 def warn_unsequenced_mod_mod : Warning<
   "multiple unsequenced modifications to %0">, InGroup<Unsequenced>;
 def warn_unsequenced_mod_use : Warning<
diff --git a/clang/lib/Analysis/UninitializedValues.cpp b/clang/lib/Analysis/UninitializedValues.cpp
index 8c9cf8dac79ed..9f031c402eddb 100644
--- a/clang/lib/Analysis/UninitializedValues.cpp
+++ b/clang/lib/Analysis/UninitializedValues.cpp
@@ -281,6 +281,7 @@ class ClassifyRefs : public StmtVisitor<ClassifyRefs> {
     Use,
     SelfInit,
     ConstRefUse,
+    ConstPtrUse,
     Ignore
   };
 
@@ -451,8 +452,9 @@ void ClassifyRefs::VisitCallExpr(CallExpr *CE) {
       const Expr *Ex = stripCasts(DC->getParentASTContext(), *I);
       const auto *UO = dyn_cast<UnaryOperator>(Ex);
       if (UO && UO->getOpcode() == UO_AddrOf)
-        Ex = UO->getSubExpr();
-      classify(Ex, Ignore);
+        classify(UO->getSubExpr(), isTrivialBody ? Ignore : ConstPtrUse);
+      else
+        classify(Ex, Ignore);
     }
   }
 }
@@ -496,6 +498,7 @@ class TransferFunctions : public StmtVisitor<TransferFunctions> {
 
   void reportUse(const Expr *ex, const VarDecl *vd);
   void reportConstRefUse(const Expr *ex, const VarDecl *vd);
+  void reportConstPtrUse(const Expr *ex, const VarDecl *vd);
 
   void VisitBinaryOperator(BinaryOperator *bo);
   void VisitBlockExpr(BlockExpr *be);
@@ -682,6 +685,15 @@ void TransferFunctions::reportConstRefUse(const Expr *ex, const VarDecl *vd) {
   }
 }
 
+void TransferFunctions::reportConstPtrUse(const Expr *ex, const VarDecl *vd) {
+  Value v = vals[vd];
+  if (isAlwaysUninit(v)) {
+    auto use = getUninitUse(ex, vd, v);
+    use.setConstPtrUse();
+    handler.handleUseOfUninitVariable(vd, use);
+  }
+}
+
 void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) {
   // This represents an initialization of the 'element' value.
   if (const auto *DS = dyn_cast<DeclStmt>(FS->getElement())) {
@@ -754,6 +766,9 @@ void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) {
   case ClassifyRefs::ConstRefUse:
     reportConstRefUse(dr, cast<VarDecl>(dr->getDecl()));
     break;
+  case ClassifyRefs::ConstPtrUse:
+    reportConstPtrUse(dr, cast<VarDecl>(dr->getDecl()));
+    break;
   }
 }
 
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index ec8acbdff3b49..11e3a3cbd7909 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -993,6 +993,13 @@ static void DiagnoseUninitializedConstRefUse(Sema &S, const VarDecl *VD,
       << VD->getDeclName() << Use.getUser()->getSourceRange();
 }
 
+/// Diagnose uninitialized const pointer usages.
+static void DiagnoseUninitializedConstPtrUse(Sema &S, const VarDecl *VD,
+                                             const UninitUse &Use) {
+  S.Diag(Use.getUser()->getBeginLoc(), diag::warn_uninit_const_pointer)
+      << VD->getDeclName() << Use.getUser()->getSourceRange();
+}
+
 /// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an
 /// uninitialized variable. This manages the different forms of diagnostic
 /// emitted for particular types of uses. Returns true if the use was diagnosed
@@ -1572,9 +1579,9 @@ class UninitValsDiagReporter : public UninitVariablesHandler {
         // guaranteed to produce them in line/column order, this will provide
         // a stable ordering.
         llvm::sort(*vec, [](const UninitUse &a, const UninitUse &b) {
-          // Move ConstRef uses to the back.
-          if (a.isConstRefUse() != b.isConstRefUse())
-            return b.isConstRefUse();
+          // Move ConstRef and ConstPtr uses to the back.
+          if (a.isConstRefOrPtrUse() != b.isConstRefOrPtrUse())
+            return b.isConstRefOrPtrUse();
           // Prefer a more confident report over a less confident one.
           if (a.getKind() != b.getKind())
             return a.getKind() > b.getKind();
@@ -1587,6 +1594,11 @@ class UninitValsDiagReporter : public UninitVariablesHandler {
             break;
           }
 
+          if (U.isConstPtrUse()) {
+            DiagnoseUninitializedConstPtrUse(S, vd, U);
+            break;
+          }
+
           // If we have self-init, downgrade all uses to 'may be uninitialized'.
           UninitUse Use = hasSelfInit ? UninitUse(U.getUser(), false) : U;
 
@@ -2820,7 +2832,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
   if (!Diags.isIgnored(diag::warn_uninit_var, D->getBeginLoc()) ||
       !Diags.isIgnored(diag::warn_sometimes_uninit_var, D->getBeginLoc()) ||
       !Diags.isIgnored(diag::warn_maybe_uninit_var, D->getBeginLoc()) ||
-      !Diags.isIgnored(diag::warn_uninit_const_reference, D->getBeginLoc())) {
+      !Diags.isIgnored(diag::warn_uninit_const_reference, D->getBeginLoc()) ||
+      !Diags.isIgnored(diag::warn_uninit_const_pointer, D->getBeginLoc())) {
     if (CFG *cfg = AC.getCFG()) {
       UninitValsDiagReporter reporter(S);
       UninitVariablesAnalysisStats stats;
diff --git a/clang/test/Misc/warning-wall.c b/clang/test/Misc/warning-wall.c
index 91de843f88c91..689868c62f6a7 100644
--- a/clang/test/Misc/warning-wall.c
+++ b/clang/test/Misc/warning-wall.c
@@ -66,6 +66,7 @@ CHECK-NEXT:    -Wuninitialized
 CHECK-NEXT:      -Wsometimes-uninitialized
 CHECK-NEXT:      -Wstatic-self-init
 CHECK-NEXT:      -Wuninitialized-const-reference
+CHECK-NEXT:      -Wuninitialized-const-pointer
 CHECK-NEXT:    -Wunknown-pragmas
 CHECK-NEXT:    -Wunused
 CHECK-NEXT:      -Wunused-argument
diff --git a/clang/test/SemaCXX/warn-uninitialized-const-pointer.cpp b/clang/test/SemaCXX/warn-uninitialized-const-pointer.cpp
new file mode 100644
index 0000000000000..62802ba7375cc
--- /dev/null
+++ b/clang/test/SemaCXX/warn-uninitialized-const-pointer.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wuninitialized-const-pointer -verify %s
+
+template <class T>
+void ignore_template(const T *) {}
+void ignore(const int *) {}
+void dont_ignore_non_empty(const int *) { ; } 
+void dont_ignore_block(const int *) { {} }
+void dont_ignore_try_block(const int *) try {
+} catch (...) {
+}
+int const_ptr_use(const int *);
+
+void f(int a) {
+  int i;
+  const_ptr_use(&i);             // expected-warning {{variable 'i' is uninitialized when passed as a const pointer argument here}}
+  int j = j + const_ptr_use(&j); // expected-warning {{variable 'j' is uninitialized when used within its own initialization}}
+  int k = k;                     // expected-warning {{variable 'k' is uninitialized when used within its own initialization}}
+  const_ptr_use(&k);
+
+  // Only report if a variable is always uninitialized at the point of use
+  int l;
+  if (a < 42)
+    l = 1;
+  const_ptr_use(&l);
+
+  // Don't report if the called function is known to be empty.
+  int m;
+  ignore_template(&m);
+  ignore(&m);
+  dont_ignore_non_empty(&m); // expected-warning {{variable 'm' is uninitialized when passed as a const pointer argument here}}
+  int n;
+  dont_ignore_block(&n); // expected-warning {{variable 'n' is uninitialized when passed as a const pointer argument here}}
+  int o;
+  dont_ignore_try_block(&o); // expected-warning {{variable 'o' is uninitialized when passed as a const pointer argument here}}
+}
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.inserters.arithmetic/pointer.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.inserters.arithmetic/pointer.pass.cpp
index 61fd0a804ecd3..f15f1b96b4b27 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.inserters.arithmetic/pointer.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.inserters.arithmetic/pointer.pass.cpp
@@ -62,14 +62,14 @@ int main(int, char**)
     {
         testbuf<char> sb1;
         std::ostream os1(&sb1);
-        int n1;
+        int n1 = 0;
         os1 << &n1;
         assert(os1.good());
         std::string s1(sb1.str());
 
         testbuf<char> sb2;
         std::ostream os2(&sb2);
-        int n2;
+        int n2 = 0;
         os2 << &n2;
         assert(os2.good());
         std::string s2(sb2.str());
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.inserters.arithmetic/pointer.volatile.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.inserters.arithmetic/pointer.volatile.pass.cpp
index 69d84f640d54e..6a1cde15a69bd 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.inserters.arithmetic/pointer.volatile.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.inserters.arithmetic/pointer.volatile.pass.cpp
@@ -61,7 +61,7 @@ class testbuf : public std::basic_streambuf<CharT> {
 int main(int, char**) {
   testbuf<char> sb1;
   std::ostream os1(&sb1);
-  int n1;
+  int n1 = 0;
   os1 << &n1;
   assert(os1.good());
   std::string s1 = sb1.str();
@@ -74,7 +74,7 @@ int main(int, char**) {
 
   testbuf<char> sb3;
   std::ostream os3(&sb3);
-  volatile int n3;
+  volatile int n3 = 0;
   os3 << &n3;
   assert(os3.good());
   std::string s3 = sb3.str();

>From 612c73f953a4c7f77806b67a518c21661998c81a Mon Sep 17 00:00:00 2001
From: Igor Kudrin <ikudrin at accesssoftek.com>
Date: Fri, 11 Jul 2025 23:29:35 -0700
Subject: [PATCH 2/2] fixup! code formatting

---
 clang/lib/Analysis/UninitializedValues.cpp | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/clang/lib/Analysis/UninitializedValues.cpp b/clang/lib/Analysis/UninitializedValues.cpp
index 9f031c402eddb..0175d4a444d4d 100644
--- a/clang/lib/Analysis/UninitializedValues.cpp
+++ b/clang/lib/Analysis/UninitializedValues.cpp
@@ -276,14 +276,7 @@ namespace {
 /// escaped the analysis and will be treated as an initialization.
 class ClassifyRefs : public StmtVisitor<ClassifyRefs> {
 public:
-  enum Class {
-    Init,
-    Use,
-    SelfInit,
-    ConstRefUse,
-    ConstPtrUse,
-    Ignore
-  };
+  enum Class { Init, Use, SelfInit, ConstRefUse, ConstPtrUse, Ignore };
 
 private:
   const DeclContext *DC;



More information about the cfe-commits mailing list