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 <ikud...@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 <ikud...@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; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits