Skip to content

Commit f0befb0

Browse files
authored
[clang] Combine ConstRefUse with other warnings for uninitialized values (llvm#147898)
This helps to avoid duplicating warnings in cases like: ``` > cat test.cpp void bar(int); void foo(const int &); void test(bool a) { int v = v; if (a) bar(v); else foo(v); } > clang++.exe test.cpp -fsyntax-only -Wuninitialized test.cpp:4:11: warning: variable 'v' is uninitialized when used within its own initialization [-Wuninitialized] 4 | int v = v; | ~ ^ test.cpp:4:11: warning: variable 'v' is uninitialized when used within its own initialization [-Wuninitialized] 4 | int v = v; | ~ ^ 2 warnings generated. ```
1 parent aa4c856 commit f0befb0

File tree

5 files changed

+50
-54
lines changed

5 files changed

+50
-54
lines changed

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ class UninitUse {
4747
/// Does this use always see an uninitialized value?
4848
bool AlwaysUninit;
4949

50+
/// Is this use a const reference to this variable?
51+
bool ConstRefUse = false;
52+
5053
/// This use is always uninitialized if it occurs after any of these branches
5154
/// is taken.
5255
SmallVector<Branch, 2> UninitBranches;
@@ -61,10 +64,13 @@ class UninitUse {
6164

6265
void setUninitAfterCall() { UninitAfterCall = true; }
6366
void setUninitAfterDecl() { UninitAfterDecl = true; }
67+
void setConstRefUse() { ConstRefUse = true; }
6468

6569
/// Get the expression containing the uninitialized use.
6670
const Expr *getUser() const { return User; }
6771

72+
bool isConstRefUse() const { return ConstRefUse; }
73+
6874
/// The kind of uninitialized use.
6975
enum Kind {
7076
/// The use might be uninitialized.
@@ -110,10 +116,6 @@ class UninitVariablesHandler {
110116
virtual void handleUseOfUninitVariable(const VarDecl *vd,
111117
const UninitUse &use) {}
112118

113-
/// Called when the uninitialized variable is used as const refernce argument.
114-
virtual void handleConstRefUseOfUninitVariable(const VarDecl *vd,
115-
const UninitUse &use) {}
116-
117119
/// Called when the uninitialized variable analysis detects the
118120
/// idiom 'int x = x'. All other uses of 'x' within the initializer
119121
/// are handled by handleUseOfUninitVariable.

clang/lib/Analysis/UninitializedValues.cpp

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -675,8 +675,11 @@ void TransferFunctions::reportUse(const Expr *ex, const VarDecl *vd) {
675675

676676
void TransferFunctions::reportConstRefUse(const Expr *ex, const VarDecl *vd) {
677677
Value v = vals[vd];
678-
if (isAlwaysUninit(v))
679-
handler.handleConstRefUseOfUninitVariable(vd, getUninitUse(ex, vd, v));
678+
if (isAlwaysUninit(v)) {
679+
auto use = getUninitUse(ex, vd, v);
680+
use.setConstRefUse();
681+
handler.handleUseOfUninitVariable(vd, use);
682+
}
680683
}
681684

682685
void TransferFunctions::VisitObjCForCollectionStmt(ObjCForCollectionStmt *FS) {
@@ -891,12 +894,6 @@ struct PruneBlocksHandler : public UninitVariablesHandler {
891894
hadAnyUse = true;
892895
}
893896

894-
void handleConstRefUseOfUninitVariable(const VarDecl *vd,
895-
const UninitUse &use) override {
896-
hadUse[currentBlock] = true;
897-
hadAnyUse = true;
898-
}
899-
900897
/// Called when the uninitialized variable analysis detects the
901898
/// idiom 'int x = x'. All other uses of 'x' within the initializer
902899
/// are handled by handleUseOfUninitVariable.

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 13 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -987,11 +987,10 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use,
987987
}
988988

989989
/// Diagnose uninitialized const reference usages.
990-
static bool DiagnoseUninitializedConstRefUse(Sema &S, const VarDecl *VD,
990+
static void DiagnoseUninitializedConstRefUse(Sema &S, const VarDecl *VD,
991991
const UninitUse &Use) {
992992
S.Diag(Use.getUser()->getBeginLoc(), diag::warn_uninit_const_reference)
993993
<< VD->getDeclName() << Use.getUser()->getSourceRange();
994-
return true;
995994
}
996995

997996
/// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an
@@ -1533,33 +1532,24 @@ class UninitValsDiagReporter : public UninitVariablesHandler {
15331532
// order of diagnostics when calling flushDiagnostics().
15341533
typedef llvm::MapVector<const VarDecl *, MappedType> UsesMap;
15351534
UsesMap uses;
1536-
UsesMap constRefUses;
15371535

15381536
public:
15391537
UninitValsDiagReporter(Sema &S) : S(S) {}
15401538
~UninitValsDiagReporter() override { flushDiagnostics(); }
15411539

1542-
MappedType &getUses(UsesMap &um, const VarDecl *vd) {
1543-
MappedType &V = um[vd];
1540+
MappedType &getUses(const VarDecl *vd) {
1541+
MappedType &V = uses[vd];
15441542
if (!V.getPointer())
15451543
V.setPointer(new UsesVec());
15461544
return V;
15471545
}
15481546

15491547
void handleUseOfUninitVariable(const VarDecl *vd,
15501548
const UninitUse &use) override {
1551-
getUses(uses, vd).getPointer()->push_back(use);
1552-
}
1553-
1554-
void handleConstRefUseOfUninitVariable(const VarDecl *vd,
1555-
const UninitUse &use) override {
1556-
getUses(constRefUses, vd).getPointer()->push_back(use);
1549+
getUses(vd).getPointer()->push_back(use);
15571550
}
15581551

1559-
void handleSelfInit(const VarDecl *vd) override {
1560-
getUses(uses, vd).setInt(true);
1561-
getUses(constRefUses, vd).setInt(true);
1562-
}
1552+
void handleSelfInit(const VarDecl *vd) override { getUses(vd).setInt(true); }
15631553

15641554
void flushDiagnostics() {
15651555
for (const auto &P : uses) {
@@ -1582,13 +1572,21 @@ class UninitValsDiagReporter : public UninitVariablesHandler {
15821572
// guaranteed to produce them in line/column order, this will provide
15831573
// a stable ordering.
15841574
llvm::sort(*vec, [](const UninitUse &a, const UninitUse &b) {
1575+
// Move ConstRef uses to the back.
1576+
if (a.isConstRefUse() != b.isConstRefUse())
1577+
return b.isConstRefUse();
15851578
// Prefer a more confident report over a less confident one.
15861579
if (a.getKind() != b.getKind())
15871580
return a.getKind() > b.getKind();
15881581
return a.getUser()->getBeginLoc() < b.getUser()->getBeginLoc();
15891582
});
15901583

15911584
for (const auto &U : *vec) {
1585+
if (U.isConstRefUse()) {
1586+
DiagnoseUninitializedConstRefUse(S, vd, U);
1587+
break;
1588+
}
1589+
15921590
// If we have self-init, downgrade all uses to 'may be uninitialized'.
15931591
UninitUse Use = hasSelfInit ? UninitUse(U.getUser(), false) : U;
15941592

@@ -1604,32 +1602,6 @@ class UninitValsDiagReporter : public UninitVariablesHandler {
16041602
}
16051603

16061604
uses.clear();
1607-
1608-
// Flush all const reference uses diags.
1609-
for (const auto &P : constRefUses) {
1610-
const VarDecl *vd = P.first;
1611-
const MappedType &V = P.second;
1612-
1613-
UsesVec *vec = V.getPointer();
1614-
bool hasSelfInit = V.getInt();
1615-
1616-
if (!vec->empty() && hasSelfInit && hasAlwaysUninitializedUse(vec))
1617-
DiagnoseUninitializedUse(S, vd,
1618-
UninitUse(vd->getInit()->IgnoreParenCasts(),
1619-
/* isAlwaysUninit */ true),
1620-
/* alwaysReportSelfInit */ true);
1621-
else {
1622-
for (const auto &U : *vec) {
1623-
if (DiagnoseUninitializedConstRefUse(S, vd, U))
1624-
break;
1625-
}
1626-
}
1627-
1628-
// Release the uses vector.
1629-
delete vec;
1630-
}
1631-
1632-
constRefUses.clear();
16331605
}
16341606

16351607
private:
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clang_cc1 -fsyntax-only -Wuninitialized -verify %s
2+
3+
void use_val(int);
4+
void use_const_ref(const int &);
5+
6+
// Test that the warning about self initialization is generated only once.
7+
void test_self_init_1warning(bool a) {
8+
int v = v; // expected-warning {{variable 'v' is uninitialized when used within its own initialization}}
9+
if (a)
10+
use_val(v);
11+
else
12+
use_const_ref(v);
13+
}
14+
15+
// Test that the diagnostic for using an uninitialized variable directly has a
16+
// higher priority than using the same variable via a const reference.
17+
void test_prioritize_use_over_const_ref(bool a) {
18+
int v; // expected-note {{initialize the variable 'v' to silence this warning}}
19+
if (a) // expected-warning {{variable 'v' is used uninitialized whenever 'if' condition is false}}
20+
// expected-note@-1 {{remove the 'if' if its condition is always true}}
21+
v = 2;
22+
else
23+
use_const_ref(v);
24+
use_val(v); // expected-note {{uninitialized use occurs here}}
25+
}

clang/test/SemaCXX/warn-uninitialized-const-reference.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ int const_use(const int i);
2727
void f(int a) {
2828
int i;
2929
const_ref_use(i); // expected-warning {{variable 'i' is uninitialized when passed as a const reference argument here}}
30-
int j = j + const_ref_use(j); // expected-warning {{variable 'j' is uninitialized when used within its own initialization}} expected-warning {{variable 'j' is uninitialized when passed as a const reference argument here}}
30+
int j = j + const_ref_use(j); // expected-warning {{variable 'j' is uninitialized when used within its own initialization}}
3131
A a1 = const_ref_use_A(a1); // expected-warning {{variable 'a1' is uninitialized when passed as a const reference argument here}}
3232
int k = const_use(k); // expected-warning {{variable 'k' is uninitialized when used within its own initialization}}
3333
A a2 = const_use_A(a2); // expected-warning {{variable 'a2' is uninitialized when used within its own initialization}}

0 commit comments

Comments
 (0)