Split a chunk of -Wconditional-uninitialized warnings out into a separate flag,
-Wsometimes-uninitialized. This detects cases where an explicitly-written branch
inevitably leads to an uninitialized variable use (so either the branch is dead
code or there is an uninitialized use bug).
This chunk of warnings tentatively lives within -Wuninitialized, in order to
give it more visibility to existing Clang users.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@157458 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp
index 01deec1..bbcd6a8 100644
--- a/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/lib/Sema/AnalysisBasedWarnings.cpp
@@ -456,16 +456,93 @@
return true;
}
+/// NoteUninitBranches -- Helper function to produce notes for branches which
+/// inevitably lead to an uninitialized variable use.
+static void NoteUninitBranches(Sema &S, const UninitUse &Use) {
+ for (UninitUse::branch_iterator I = Use.branch_begin(), E = Use.branch_end();
+ I != E; ++I) {
+ const Stmt *Term = I->Terminator;
+ unsigned DiagKind;
+ SourceRange Range;
+ const char *Str;
+ switch (Term->getStmtClass()) {
+ default:
+ // Don't know how to report this.
+ continue;
+
+ // "condition is true / condition is false".
+ case Stmt::IfStmtClass:
+ DiagKind = 0;
+ Str = "if";
+ Range = cast<IfStmt>(Term)->getCond()->getSourceRange();
+ break;
+ case Stmt::ConditionalOperatorClass:
+ DiagKind = 0;
+ Str = "?:";
+ Range = cast<ConditionalOperator>(Term)->getCond()->getSourceRange();
+ break;
+ case Stmt::BinaryOperatorClass: {
+ const BinaryOperator *BO = cast<BinaryOperator>(Term);
+ if (!BO->isLogicalOp())
+ continue;
+ DiagKind = 0;
+ Str = BO->getOpcodeStr();
+ Range = BO->getLHS()->getSourceRange();
+ break;
+ }
+
+ // "loop is entered / loop is exited".
+ case Stmt::WhileStmtClass:
+ DiagKind = 1;
+ Str = "while";
+ Range = cast<WhileStmt>(Term)->getCond()->getSourceRange();
+ break;
+ case Stmt::ForStmtClass:
+ DiagKind = 1;
+ Str = "for";
+ Range = cast<ForStmt>(Term)->getCond()->getSourceRange();
+ break;
+ case Stmt::CXXForRangeStmtClass:
+ DiagKind = 1;
+ Str = "for";
+ Range = cast<CXXForRangeStmt>(Term)->getCond()->getSourceRange();
+ break;
+
+ // "condition is true / loop is exited".
+ case Stmt::DoStmtClass:
+ DiagKind = 2;
+ Str = "do";
+ Range = cast<DoStmt>(Term)->getCond()->getSourceRange();
+ break;
+
+ // "switch case is taken".
+ case Stmt::CaseStmtClass:
+ DiagKind = 3;
+ Str = "case";
+ Range = cast<CaseStmt>(Term)->getLHS()->getSourceRange();
+ break;
+ case Stmt::DefaultStmtClass:
+ DiagKind = 3;
+ Str = "default";
+ Range = cast<DefaultStmt>(Term)->getDefaultLoc();
+ break;
+ }
+
+ S.Diag(Range.getBegin(), diag::note_sometimes_uninit_var_branch)
+ << DiagKind << Str << I->Output << Range;
+ }
+}
+
/// 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
-/// as a warning. If a pariticular use is one we omit warnings for, returns
+/// as a warning. If a particular use is one we omit warnings for, returns
/// false.
static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD,
- const Expr *E, bool isAlwaysUninit,
+ const UninitUse &Use,
bool alwaysReportSelfInit = false) {
- if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Use.getUser())) {
// Inspect the initializer of the variable declaration which is
// being referenced prior to its initialization. We emit
// specialized diagnostics for self-initialization, and we
@@ -491,20 +568,37 @@
}
}
- S.Diag(DRE->getLocStart(), isAlwaysUninit ? diag::warn_uninit_var
- : diag::warn_maybe_uninit_var)
+ unsigned DiagID = 0;
+ switch (Use.getKind()) {
+ case UninitUse::Always: DiagID = diag::warn_uninit_var; break;
+ case UninitUse::Sometimes: DiagID = diag::warn_sometimes_uninit_var; break;
+ case UninitUse::Maybe: DiagID = diag::warn_maybe_uninit_var; break;
+ }
+ S.Diag(DRE->getLocStart(), DiagID)
<< VD->getDeclName() << DRE->getSourceRange();
+ NoteUninitBranches(S, Use);
} else {
- const BlockExpr *BE = cast<BlockExpr>(E);
+ const BlockExpr *BE = cast<BlockExpr>(Use.getUser());
if (VD->getType()->isBlockPointerType() &&
!VD->hasAttr<BlocksAttr>())
S.Diag(BE->getLocStart(), diag::warn_uninit_byref_blockvar_captured_by_block)
<< VD->getDeclName();
- else
- S.Diag(BE->getLocStart(),
- isAlwaysUninit ? diag::warn_uninit_var_captured_by_block
- : diag::warn_maybe_uninit_var_captured_by_block)
- << VD->getDeclName();
+ else {
+ unsigned DiagID = 0;
+ switch (Use.getKind()) {
+ case UninitUse::Always:
+ DiagID = diag::warn_uninit_var_captured_by_block;
+ break;
+ case UninitUse::Sometimes:
+ DiagID = diag::warn_sometimes_uninit_var_captured_by_block;
+ break;
+ case UninitUse::Maybe:
+ DiagID = diag::warn_maybe_uninit_var_captured_by_block;
+ break;
+ }
+ S.Diag(BE->getLocStart(), DiagID) << VD->getDeclName();
+ NoteUninitBranches(S, Use);
+ }
}
// Report where the variable was declared when the use wasn't within
@@ -700,13 +794,14 @@
}
-typedef std::pair<const Expr*, bool> UninitUse;
-
namespace {
struct SLocSort {
bool operator()(const UninitUse &a, const UninitUse &b) {
- SourceLocation aLoc = a.first->getLocStart();
- SourceLocation bLoc = b.first->getLocStart();
+ // Prefer a more confident report over a less confident one.
+ if (a.getKind() != b.getKind())
+ return a.getKind() > b.getKind();
+ SourceLocation aLoc = a.getUser()->getLocStart();
+ SourceLocation bLoc = b.getUser()->getLocStart();
return aLoc.getRawEncoding() < bLoc.getRawEncoding();
}
};
@@ -735,9 +830,8 @@
return V;
}
- void handleUseOfUninitVariable(const Expr *ex, const VarDecl *vd,
- bool isAlwaysUninit) {
- getUses(vd).first->push_back(std::make_pair(ex, isAlwaysUninit));
+ void handleUseOfUninitVariable(const VarDecl *vd, const UninitUse &use) {
+ getUses(vd).first->push_back(use);
}
void handleSelfInit(const VarDecl *vd) {
@@ -761,8 +855,9 @@
// variable, but the root cause is an idiomatic self-init. We want
// to report the diagnostic at the self-init since that is the root cause.
if (!vec->empty() && hasSelfInit && hasAlwaysUninitializedUse(vec))
- DiagnoseUninitializedUse(S, vd, vd->getInit()->IgnoreParenCasts(),
- /* isAlwaysUninit */ true,
+ DiagnoseUninitializedUse(S, vd,
+ UninitUse(vd->getInit()->IgnoreParenCasts(),
+ /* isAlwaysUninit */ true),
/* alwaysReportSelfInit */ true);
else {
// Sort the uses by their SourceLocations. While not strictly
@@ -772,8 +867,10 @@
for (UsesVec::iterator vi = vec->begin(), ve = vec->end(); vi != ve;
++vi) {
- if (DiagnoseUninitializedUse(S, vd, vi->first,
- /*isAlwaysUninit=*/vi->second))
+ // If we have self-init, downgrade all uses to 'may be uninitialized'.
+ UninitUse Use = hasSelfInit ? UninitUse(vi->getUser(), false) : *vi;
+
+ if (DiagnoseUninitializedUse(S, vd, Use))
// Skip further diagnostics for this variable. We try to warn only
// on the first point at which a variable is used uninitialized.
break;
@@ -789,7 +886,7 @@
private:
static bool hasAlwaysUninitializedUse(const UsesVec* vec) {
for (UsesVec::const_iterator i = vec->begin(), e = vec->end(); i != e; ++i) {
- if (i->second) {
+ if (i->getKind() == UninitUse::Always) {
return true;
}
}
@@ -1131,6 +1228,8 @@
if (Diags.getDiagnosticLevel(diag::warn_uninit_var, D->getLocStart())
!= DiagnosticsEngine::Ignored ||
+ Diags.getDiagnosticLevel(diag::warn_sometimes_uninit_var,D->getLocStart())
+ != DiagnosticsEngine::Ignored ||
Diags.getDiagnosticLevel(diag::warn_maybe_uninit_var, D->getLocStart())
!= DiagnosticsEngine::Ignored) {
if (CFG *cfg = AC.getCFG()) {