[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