[clang] Fix suppressing diagnostics for uninitialized variables (#148336)

When one kind of diagnostics is disabled, this should not preclude other
diagnostics from displaying, even if they have lower priority. For
example, this should print a warning about passing an uninitialized
variable as a const reference:
```
> cat test.cpp
void foo(const int &);
int f(bool a) {
  int v;
  if (a) {
    foo(v);
    v = 5;
  }
  return v;
}
> clang test.cpp -fsyntax-only -Wuninitialized -Wno-sometimes-uninitialized
```
This commit is contained in:
Igor Kudrin 2025-07-14 14:01:11 -07:00 committed by GitHub
parent ada514b0af
commit 2464313eef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 60 additions and 40 deletions

View File

@ -987,10 +987,11 @@ static void DiagUninitUse(Sema &S, const VarDecl *VD, const UninitUse &Use,
} }
/// Diagnose uninitialized const reference usages. /// Diagnose uninitialized const reference usages.
static void DiagnoseUninitializedConstRefUse(Sema &S, const VarDecl *VD, static bool DiagnoseUninitializedConstRefUse(Sema &S, const VarDecl *VD,
const UninitUse &Use) { const UninitUse &Use) {
S.Diag(Use.getUser()->getBeginLoc(), diag::warn_uninit_const_reference) S.Diag(Use.getUser()->getBeginLoc(), diag::warn_uninit_const_reference)
<< VD->getDeclName() << Use.getUser()->getSourceRange(); << VD->getDeclName() << Use.getUser()->getSourceRange();
return !S.getDiagnostics().isLastDiagnosticIgnored();
} }
/// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an /// DiagnoseUninitializedUse -- Helper function for diagnosing uses of an
@ -1022,7 +1023,7 @@ static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD,
if (CR.doesContainReference()) { if (CR.doesContainReference()) {
S.Diag(DRE->getBeginLoc(), diag::warn_uninit_self_reference_in_init) S.Diag(DRE->getBeginLoc(), diag::warn_uninit_self_reference_in_init)
<< VD->getDeclName() << VD->getLocation() << DRE->getSourceRange(); << VD->getDeclName() << VD->getLocation() << DRE->getSourceRange();
return true; return !S.getDiagnostics().isLastDiagnosticIgnored();
} }
} }
@ -1045,7 +1046,7 @@ static bool DiagnoseUninitializedUse(Sema &S, const VarDecl *VD,
S.Diag(VD->getBeginLoc(), diag::note_var_declared_here) S.Diag(VD->getBeginLoc(), diag::note_var_declared_here)
<< VD->getDeclName(); << VD->getDeclName();
return true; return !S.getDiagnostics().isLastDiagnosticIgnored();
} }
namespace { namespace {
@ -1559,43 +1560,7 @@ public:
UsesVec *vec = V.getPointer(); UsesVec *vec = V.getPointer();
bool hasSelfInit = V.getInt(); bool hasSelfInit = V.getInt();
// Specially handle the case where we have uses of an uninitialized diagnoseUnitializedVar(vd, hasSelfInit, vec);
// 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,
UninitUse(vd->getInit()->IgnoreParenCasts(),
/* isAlwaysUninit */ true),
/* alwaysReportSelfInit */ true);
else {
// Sort the uses by their SourceLocations. While not strictly
// 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();
// Prefer a more confident report over a less confident one.
if (a.getKind() != b.getKind())
return a.getKind() > b.getKind();
return a.getUser()->getBeginLoc() < b.getUser()->getBeginLoc();
});
for (const auto &U : *vec) {
if (U.isConstRefUse()) {
DiagnoseUninitializedConstRefUse(S, vd, U);
break;
}
// If we have self-init, downgrade all uses to 'may be uninitialized'.
UninitUse Use = hasSelfInit ? UninitUse(U.getUser(), false) : U;
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;
}
}
// Release the uses vector. // Release the uses vector.
delete vec; delete vec;
@ -1612,6 +1577,49 @@ private:
U.getKind() == UninitUse::AfterDecl; U.getKind() == UninitUse::AfterDecl;
}); });
} }
// Print the diagnostic for the variable. We try to warn only on the first
// point at which a variable is used uninitialized. After the first
// diagnostic is printed, further diagnostics for this variable are skipped.
void diagnoseUnitializedVar(const VarDecl *vd, bool hasSelfInit,
UsesVec *vec) {
// Specially handle the case where we have uses of an uninitialized
// 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 (hasSelfInit && hasAlwaysUninitializedUse(vec)) {
if (DiagnoseUninitializedUse(S, vd,
UninitUse(vd->getInit()->IgnoreParenCasts(),
/*isAlwaysUninit=*/true),
/*alwaysReportSelfInit=*/true))
return;
}
// Sort the uses by their SourceLocations. While not strictly
// guaranteed to produce them in line/column order, this will provide
// a stable ordering.
llvm::sort(*vec, [](const UninitUse &a, const UninitUse &b) {
// Prefer the direct use of an uninitialized variable over its use via
// constant reference.
if (a.isConstRefUse() != b.isConstRefUse())
return b.isConstRefUse();
// Prefer a more confident report over a less confident one.
if (a.getKind() != b.getKind())
return a.getKind() > b.getKind();
return a.getUser()->getBeginLoc() < b.getUser()->getBeginLoc();
});
for (const auto &U : *vec) {
if (U.isConstRefUse()) {
if (DiagnoseUninitializedConstRefUse(S, vd, U))
return;
} else {
// If we have self-init, downgrade all uses to 'may be uninitialized'.
UninitUse Use = hasSelfInit ? UninitUse(U.getUser(), false) : U;
if (DiagnoseUninitializedUse(S, vd, Use))
return;
}
}
}
}; };
/// Inter-procedural data for the called-once checker. /// Inter-procedural data for the called-once checker.

View File

@ -0,0 +1,12 @@
// RUN: %clang_cc1 -fsyntax-only -Wuninitialized -Wno-sometimes-uninitialized -verify %s
void foo(const int &);
int f(bool a) {
int v;
if (a) {
foo(v); // expected-warning {{variable 'v' is uninitialized when passed as a const reference argument here}}
v = 5;
}
return v;
}