Skip to content

Commit 00dacf8

Browse files
authored
[clang] Add -Wuninitialized-const-pointer (#148337)
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); } ``` This is an extract from #147221 as suggested in [this comment](#147221 (comment)).
1 parent 244ebef commit 00dacf8

File tree

9 files changed

+86
-18
lines changed

9 files changed

+86
-18
lines changed

clang/include/clang/Analysis/Analyses/UninitializedValues.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ class UninitUse {
5050
/// Is this use a const reference to this variable?
5151
bool ConstRefUse = false;
5252

53+
/// Is this use a const pointer to this variable?
54+
bool ConstPtrUse = false;
55+
5356
/// This use is always uninitialized if it occurs after any of these branches
5457
/// is taken.
5558
SmallVector<Branch, 2> UninitBranches;
@@ -65,11 +68,14 @@ class UninitUse {
6568
void setUninitAfterCall() { UninitAfterCall = true; }
6669
void setUninitAfterDecl() { UninitAfterDecl = true; }
6770
void setConstRefUse() { ConstRefUse = true; }
71+
void setConstPtrUse() { ConstPtrUse = true; }
6872

6973
/// Get the expression containing the uninitialized use.
7074
const Expr *getUser() const { return User; }
7175

7276
bool isConstRefUse() const { return ConstRefUse; }
77+
bool isConstPtrUse() const { return ConstPtrUse; }
78+
bool isConstRefOrPtrUse() const { return ConstRefUse || ConstPtrUse; }
7379

7480
/// The kind of uninitialized use.
7581
enum Kind {

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -952,9 +952,11 @@ def UninitializedMaybe : DiagGroup<"conditional-uninitialized">;
952952
def UninitializedSometimes : DiagGroup<"sometimes-uninitialized">;
953953
def UninitializedStaticSelfInit : DiagGroup<"static-self-init">;
954954
def UninitializedConstReference : DiagGroup<"uninitialized-const-reference">;
955+
def UninitializedConstPointer : DiagGroup<"uninitialized-const-pointer">;
955956
def Uninitialized : DiagGroup<"uninitialized", [UninitializedSometimes,
956957
UninitializedStaticSelfInit,
957-
UninitializedConstReference]>;
958+
UninitializedConstReference,
959+
UninitializedConstPointer]>;
958960
def IgnoredPragmaIntrinsic : DiagGroup<"ignored-pragma-intrinsic">;
959961
// #pragma optimize is often used to avoid to work around MSVC codegen bugs or
960962
// to disable inlining. It's not completely clear what alternative to suggest

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2548,6 +2548,10 @@ def warn_uninit_const_reference : Warning<
25482548
"variable %0 is uninitialized when passed as a const reference argument "
25492549
"here">, InGroup<UninitializedConstReference>, DefaultIgnore;
25502550

2551+
def warn_uninit_const_pointer : Warning<
2552+
"variable %0 is uninitialized when passed as a const pointer argument here">,
2553+
InGroup<UninitializedConstPointer>, DefaultIgnore;
2554+
25512555
def warn_unsequenced_mod_mod : Warning<
25522556
"multiple unsequenced modifications to %0">, InGroup<Unsequenced>;
25532557
def warn_unsequenced_mod_use : Warning<

clang/lib/Analysis/UninitializedValues.cpp

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,7 @@ namespace {
276276
/// escaped the analysis and will be treated as an initialization.
277277
class ClassifyRefs : public StmtVisitor<ClassifyRefs> {
278278
public:
279-
enum Class {
280-
Init,
281-
Use,
282-
SelfInit,
283-
ConstRefUse,
284-
Ignore
285-
};
279+
enum Class { Init, Use, SelfInit, ConstRefUse, ConstPtrUse, Ignore };
286280

287281
private:
288282
const DeclContext *DC;
@@ -451,8 +445,9 @@ void ClassifyRefs::VisitCallExpr(CallExpr *CE) {
451445
const Expr *Ex = stripCasts(DC->getParentASTContext(), *I);
452446
const auto *UO = dyn_cast<UnaryOperator>(Ex);
453447
if (UO && UO->getOpcode() == UO_AddrOf)
454-
Ex = UO->getSubExpr();
455-
classify(Ex, Ignore);
448+
classify(UO->getSubExpr(), isTrivialBody ? Ignore : ConstPtrUse);
449+
else
450+
classify(Ex, Ignore);
456451
}
457452
}
458453
}
@@ -496,6 +491,7 @@ class TransferFunctions : public StmtVisitor<TransferFunctions> {
496491

497492
void reportUse(const Expr *ex, const VarDecl *vd);
498493
void reportConstRefUse(const Expr *ex, const VarDecl *vd);
494+
void reportConstPtrUse(const Expr *ex, const VarDecl *vd);
499495

500496
void VisitBinaryOperator(BinaryOperator *bo);
501497
void VisitBlockExpr(BlockExpr *be);
@@ -682,6 +678,15 @@ void TransferFunctions::reportConstRefUse(const Expr *ex, const VarDecl *vd) {
682678
}
683679
}
684680

681+
void TransferFunctions::reportConstPtrUse(const Expr *ex, const VarDecl *vd) {
682+
Value v = vals[vd];
683+
if (isAlwaysUninit(v)) {
684+
auto use = getUninitUse(ex, vd, v);
685+
use.setConstPtrUse();
686+
handler.handleUseOfUninitVariable(vd, use);
687+
}
688+
}
689+
685690
void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) {
686691
// This represents an initialization of the 'element' value.
687692
if (const auto *DS = dyn_cast<DeclStmt>(FS->getElement())) {
@@ -754,6 +759,9 @@ void TransferFunctions::VisitDeclRefExpr(DeclRefExpr *dr) {
754759
case ClassifyRefs::ConstRefUse:
755760
reportConstRefUse(dr, cast<VarDecl>(dr->getDecl()));
756761
break;
762+
case ClassifyRefs::ConstPtrUse:
763+
reportConstPtrUse(dr, cast<VarDecl>(dr->getDecl()));
764+
break;
757765
}
758766
}
759767

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,14 @@ static bool DiagnoseUninitializedConstRefUse(Sema &S, const VarDecl *VD,
994994
return !S.getDiagnostics().isLastDiagnosticIgnored();
995995
}
996996

997+
/// Diagnose uninitialized const pointer usages.
998+
static bool DiagnoseUninitializedConstPtrUse(Sema &S, const VarDecl *VD,
999+
const UninitUse &Use) {
1000+
S.Diag(Use.getUser()->getBeginLoc(), diag::warn_uninit_const_pointer)
1001+
<< VD->getDeclName() << Use.getUser()->getSourceRange();
1002+
return !S.getDiagnostics().isLastDiagnosticIgnored();
1003+
}
1004+
9971005
/// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an
9981006
/// uninitialized variable. This manages the different forms of diagnostic
9991007
/// emitted for particular types of uses. Returns true if the use was diagnosed
@@ -1599,9 +1607,9 @@ class UninitValsDiagReporter : public UninitVariablesHandler {
15991607
// a stable ordering.
16001608
llvm::sort(*vec, [](const UninitUse &a, const UninitUse &b) {
16011609
// Prefer the direct use of an uninitialized variable over its use via
1602-
// constant reference.
1603-
if (a.isConstRefUse() != b.isConstRefUse())
1604-
return b.isConstRefUse();
1610+
// constant reference or pointer.
1611+
if (a.isConstRefOrPtrUse() != b.isConstRefOrPtrUse())
1612+
return b.isConstRefOrPtrUse();
16051613
// Prefer a more confident report over a less confident one.
16061614
if (a.getKind() != b.getKind())
16071615
return a.getKind() > b.getKind();
@@ -1612,6 +1620,9 @@ class UninitValsDiagReporter : public UninitVariablesHandler {
16121620
if (U.isConstRefUse()) {
16131621
if (DiagnoseUninitializedConstRefUse(S, vd, U))
16141622
return;
1623+
} else if (U.isConstPtrUse()) {
1624+
if (DiagnoseUninitializedConstPtrUse(S, vd, U))
1625+
return;
16151626
} else {
16161627
// If we have self-init, downgrade all uses to 'may be uninitialized'.
16171628
UninitUse Use = hasSelfInit ? UninitUse(U.getUser(), false) : U;
@@ -2828,7 +2839,8 @@ void clang::sema::AnalysisBasedWarnings::IssueWarnings(
28282839
if (!Diags.isIgnored(diag::warn_uninit_var, D->getBeginLoc()) ||
28292840
!Diags.isIgnored(diag::warn_sometimes_uninit_var, D->getBeginLoc()) ||
28302841
!Diags.isIgnored(diag::warn_maybe_uninit_var, D->getBeginLoc()) ||
2831-
!Diags.isIgnored(diag::warn_uninit_const_reference, D->getBeginLoc())) {
2842+
!Diags.isIgnored(diag::warn_uninit_const_reference, D->getBeginLoc()) ||
2843+
!Diags.isIgnored(diag::warn_uninit_const_pointer, D->getBeginLoc())) {
28322844
if (CFG *cfg = AC.getCFG()) {
28332845
UninitValsDiagReporter reporter(S);
28342846
UninitVariablesAnalysisStats stats;

clang/test/Misc/warning-wall.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ CHECK-NEXT: -Wuninitialized
6666
CHECK-NEXT: -Wsometimes-uninitialized
6767
CHECK-NEXT: -Wstatic-self-init
6868
CHECK-NEXT: -Wuninitialized-const-reference
69+
CHECK-NEXT: -Wuninitialized-const-pointer
6970
CHECK-NEXT: -Wunknown-pragmas
7071
CHECK-NEXT: -Wunused
7172
CHECK-NEXT: -Wunused-argument
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -Wuninitialized-const-pointer -verify %s
2+
3+
template <class T>
4+
void ignore_template(const T *) {}
5+
void ignore(const int *) {}
6+
void dont_ignore_non_empty(const int *) { ; }
7+
void dont_ignore_block(const int *) { {} }
8+
void dont_ignore_try_block(const int *) try {
9+
} catch (...) {
10+
}
11+
int const_ptr_use(const int *);
12+
13+
void f(int a) {
14+
int i;
15+
const_ptr_use(&i); // expected-warning {{variable 'i' is uninitialized when passed as a const pointer argument here}}
16+
int j = j + const_ptr_use(&j); // expected-warning {{variable 'j' is uninitialized when used within its own initialization}}
17+
int k = k; // expected-warning {{variable 'k' is uninitialized when used within its own initialization}}
18+
const_ptr_use(&k);
19+
20+
// Only report if a variable is always uninitialized at the point of use
21+
int l;
22+
if (a < 42)
23+
l = 1;
24+
const_ptr_use(&l);
25+
26+
// Don't report if the called function is known to be empty.
27+
int m;
28+
ignore_template(&m);
29+
ignore(&m);
30+
dont_ignore_non_empty(&m); // expected-warning {{variable 'm' is uninitialized when passed as a const pointer argument here}}
31+
int n;
32+
dont_ignore_block(&n); // expected-warning {{variable 'n' is uninitialized when passed as a const pointer argument here}}
33+
int o;
34+
dont_ignore_try_block(&o); // expected-warning {{variable 'o' is uninitialized when passed as a const pointer argument here}}
35+
}

libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.inserters.arithmetic/pointer.pass.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,14 @@ int main(int, char**)
6262
{
6363
testbuf<char> sb1;
6464
std::ostream os1(&sb1);
65-
int n1;
65+
int n1 = 0;
6666
os1 << &n1;
6767
assert(os1.good());
6868
std::string s1(sb1.str());
6969

7070
testbuf<char> sb2;
7171
std::ostream os2(&sb2);
72-
int n2;
72+
int n2 = 0;
7373
os2 << &n2;
7474
assert(os2.good());
7575
std::string s2(sb2.str());

libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.inserters.arithmetic/pointer.volatile.pass.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class testbuf : public std::basic_streambuf<CharT> {
6161
int main(int, char**) {
6262
testbuf<char> sb1;
6363
std::ostream os1(&sb1);
64-
int n1;
64+
int n1 = 0;
6565
os1 << &n1;
6666
assert(os1.good());
6767
std::string s1 = sb1.str();
@@ -74,7 +74,7 @@ int main(int, char**) {
7474

7575
testbuf<char> sb3;
7676
std::ostream os3(&sb3);
77-
volatile int n3;
77+
volatile int n3 = 0;
7878
os3 << &n3;
7979
assert(os3.good());
8080
std::string s3 = sb3.str();

0 commit comments

Comments
 (0)