[clang] [Clang] Add -Wtrivial-auto-var-init warning for unreachable variables (PR #178318)
Justin Stitt via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 5 16:49:40 PST 2026
================
@@ -69,66 +69,120 @@ using namespace clang;
// Unreachable code analysis.
//===----------------------------------------------------------------------===//
+static bool isTrivialInitializer(const Expr *Init) {
+ if (!Init)
+ return true;
+ const auto *CE = dyn_cast<CXXConstructExpr>(Init);
+ if (!CE)
+ return false;
+ const auto *Ctor = CE->getConstructor();
+ return Ctor && Ctor->isTrivial() && Ctor->isDefaultConstructor() &&
+ !CE->requiresZeroInitialization();
+}
+
+static bool wouldTrivialAutoVarInitApply(const LangOptions &LangOpts,
+ const VarDecl *VD) {
+ if (LangOpts.getTrivialAutoVarInit() ==
+ LangOptions::TrivialAutoVarInitKind::Uninitialized)
+ return false;
+ if (VD->isConstexpr() || VD->hasAttr<UninitializedAttr>())
+ return false;
+ if (const auto *TD = VD->getType()->getAsTagDecl())
+ if (TD->hasAttr<NoTrivialAutoVarInitAttr>())
+ return false;
+ if (const auto *FD = dyn_cast<FunctionDecl>(VD->getDeclContext()))
+ if (FD->hasAttr<NoTrivialAutoVarInitAttr>())
+ return false;
+ return true;
+}
+
namespace {
- class UnreachableCodeHandler : public reachable_code::Callback {
- Sema &S;
- SourceRange PreviousSilenceableCondVal;
-
- public:
- UnreachableCodeHandler(Sema &s) : S(s) {}
-
- void HandleUnreachable(reachable_code::UnreachableKind UK, SourceLocation L,
- SourceRange SilenceableCondVal, SourceRange R1,
- SourceRange R2, bool HasFallThroughAttr) override {
- // If the diagnosed code is `[[fallthrough]];` and
- // `-Wunreachable-code-fallthrough` is enabled, suppress `code will never
- // be executed` warning to avoid generating diagnostic twice
- if (HasFallThroughAttr &&
- !S.getDiagnostics().isIgnored(diag::warn_unreachable_fallthrough_attr,
- SourceLocation()))
- return;
+class UnreachableCodeHandler : public reachable_code::Callback {
+ Sema &S;
+ SourceRange PreviousSilenceableCondVal;
+ bool CheckTrivialAutoVarInit;
- // Avoid reporting multiple unreachable code diagnostics that are
- // triggered by the same conditional value.
- if (PreviousSilenceableCondVal.isValid() &&
- SilenceableCondVal.isValid() &&
- PreviousSilenceableCondVal == SilenceableCondVal)
- return;
- PreviousSilenceableCondVal = SilenceableCondVal;
+public:
+ UnreachableCodeHandler(Sema &S, bool CheckTrivialAutoVarInit)
+ : S(S), CheckTrivialAutoVarInit(CheckTrivialAutoVarInit) {}
+
+ void HandleUnreachable(reachable_code::UnreachableKind UK, SourceLocation L,
+ SourceRange SilenceableCondVal, SourceRange R1,
+ SourceRange R2, bool HasFallThroughAttr) override {
+ // If the diagnosed code is `[[fallthrough]];` and
+ // `-Wunreachable-code-fallthrough` is enabled, suppress `code will never
+ // be executed` warning to avoid generating diagnostic twice
+ if (HasFallThroughAttr &&
+ !S.getDiagnostics().isIgnored(diag::warn_unreachable_fallthrough_attr,
+ SourceLocation()))
+ return;
- unsigned diag = diag::warn_unreachable;
- switch (UK) {
- case reachable_code::UK_Break:
- diag = diag::warn_unreachable_break;
- break;
- case reachable_code::UK_Return:
- diag = diag::warn_unreachable_return;
- break;
- case reachable_code::UK_Loop_Increment:
- diag = diag::warn_unreachable_loop_increment;
- break;
- case reachable_code::UK_Other:
- break;
- }
+ // Avoid reporting multiple unreachable code diagnostics that are
+ // triggered by the same conditional value.
+ if (PreviousSilenceableCondVal.isValid() && SilenceableCondVal.isValid() &&
+ PreviousSilenceableCondVal == SilenceableCondVal)
+ return;
+ PreviousSilenceableCondVal = SilenceableCondVal;
+
+ unsigned DiagID = diag::warn_unreachable;
+ switch (UK) {
+ case reachable_code::UK_Break:
+ DiagID = diag::warn_unreachable_break;
+ break;
+ case reachable_code::UK_Return:
+ DiagID = diag::warn_unreachable_return;
+ break;
+ case reachable_code::UK_Loop_Increment:
+ DiagID = diag::warn_unreachable_loop_increment;
+ break;
+ case reachable_code::UK_Other:
+ break;
+ }
- S.Diag(L, diag) << R1 << R2;
+ S.Diag(L, DiagID) << R1 << R2;
- SourceLocation Open = SilenceableCondVal.getBegin();
- if (Open.isValid()) {
- SourceLocation Close = SilenceableCondVal.getEnd();
- Close = S.getLocForEndOfToken(Close);
- if (Close.isValid()) {
- S.Diag(Open, diag::note_unreachable_silence)
+ SourceLocation Open = SilenceableCondVal.getBegin();
+ if (Open.isValid()) {
+ SourceLocation Close = S.getLocForEndOfToken(SilenceableCondVal.getEnd());
+ if (Close.isValid()) {
+ S.Diag(Open, diag::note_unreachable_silence)
<< FixItHint::CreateInsertion(Open, "/* DISABLES CODE */ (")
<< FixItHint::CreateInsertion(Close, ")");
- }
}
}
- };
+ }
+
+ void HandleUnreachableBlock(const CFGBlock *B) override {
+ // We only currently use this method for -Wtrivial-auto-var-init
+ if (!CheckTrivialAutoVarInit)
+ return;
+
+ for (const CFGElement &Elem : *B) {
+ auto CS = Elem.getAs<CFGStmt>();
+ if (!CS)
+ continue;
+ const auto *DS = dyn_cast<DeclStmt>(CS->getStmt());
+ if (!DS)
+ continue;
+ for (const Decl *DI : DS->decls()) {
+ const auto *VD = dyn_cast<VarDecl>(DI);
+ if (!VD || !VD->getDeclName() || !VD->hasLocalStorage())
----------------
JustinStitt wrote:
I think this example can produce a nameless vardecl:
```cpp
auto [x, y] = std::make_pair(1, 2);
```
where the AST shows us
```cpp
`-DecompositionDecl 0x55b7137b6118 <col:3> col:8 invalid 'auto'
|-BindingDecl ...
`-BindingDecl ...
```
noting that `DecompositionDecl` inherits from `VarDecl` proper. I could perceive a code path where `VD->getDeclName()` can return `''`.
To be frank, I copied the code from `Sema::DiagnoseUnusedButSetDecl` before proving to myself that it was even possible.
```c
Sema::DiagnoseUnusedButSetDecl:
if (!VD->isReferenced() || !VD->getDeclName() || VD->hasAttr<CleanupAttr>())
return;
```
https://github.com/llvm/llvm-project/pull/178318
More information about the cfe-commits
mailing list