[llvm-branch-commits] [clang] [Clang][OBT] Add overflow handler diagnostics (PR #235047)
Ambrose Leeb via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Oct 23 11:46:29 PDT 2026
================
@@ -672,6 +673,131 @@ void Sema::NoteOverflowCheckedOps(Expr *E) {
Handler->setReferenced();
}
+namespace {
+/// Walks a function body, including nested lambda and block bodies, and
+/// diagnoses overflow-checked operations with no visible overflow handler.
+class OverflowHandlerDiagnoser {
+ Sema &S;
+ const IdentifierInfo *Name;
+ /// Handlers visible in the current function body, innermost last. The first
+ /// entry is the function-level handler and may be null.
+ SmallVector<LabelDecl *, 4> Handlers;
+ /// Matches the %select in warn_overflow_behavior_unreachable_handler.
+ enum NestedBodyKind { NB_Lambda, NB_Block };
+ /// Innermost handler of an enclosing function, when walking a nested body.
+ const LabelDecl *EnclosingHandler = nullptr;
+ NestedBodyKind NestedKind = NB_Lambda;
+ /// Set while walking the operands of a diagnosed operation, so that one
+ /// expression yields one warning.
+ bool InDiagnosedOp = false;
+
+public:
+ OverflowHandlerDiagnoser(Sema &S)
+ : S(S), Name(getOverflowHandlerName(S.Context)) {}
+
+ void visitBody(const Decl *D, const Stmt *Body) {
+ Handlers.push_back(findFunctionOverflowHandler(S.Context, D));
+ visit(Body);
+ Handlers.pop_back();
+ }
+
+private:
+ void visitNestedBody(const Decl *D, const Stmt *Body, NestedBodyKind Kind) {
+ const LabelDecl *Outer = Handlers.back();
+ SaveAndRestore SaveHandlers(Handlers, {});
+ SaveAndRestore SaveEnclosing(EnclosingHandler,
+ Outer ? Outer : EnclosingHandler);
+ SaveAndRestore SaveKind(NestedKind, Kind);
+ SaveAndRestore SaveInDiagnosedOp(InDiagnosedOp, false);
+ visitBody(D, Body);
+ }
+
+ void visit(const Stmt *St) {
+ if (!St)
+ return;
+
+ bool Diagnosed = false;
+ if (const auto *E = dyn_cast<Expr>(St);
+ E && !InDiagnosedOp && !Handlers.back() &&
+ isOverflowHandlerCheckedOp(S.Context, E)) {
+ Diagnosed = true;
+ if (EnclosingHandler) {
+ S.Diag(E->getExprLoc(),
+ diag::warn_overflow_behavior_unreachable_handler)
+ << NestedKind << EnclosingHandler->getName();
+ S.Diag(EnclosingHandler->getLocation(),
+ diag::note_overflow_behavior_enclosing_handler);
+ } else {
+ S.Diag(E->getExprLoc(), diag::warn_overflow_behavior_no_handler)
+ << Name->getName();
+ }
+ }
+
+ if (const auto *LE = dyn_cast<LambdaExpr>(St)) {
+ for (const Expr *Init : LE->capture_inits())
+ visit(Init);
+ visitNestedBody(LE->getCallOperator(), LE->getBody(), NB_Lambda);
+ return;
+ }
+ if (const auto *BE = dyn_cast<BlockExpr>(St)) {
+ visitNestedBody(BE->getBlockDecl(), BE->getBody(), NB_Block);
+ return;
+ }
+
+ // Variable length array sizes and the initializers of static locals
+ // trap regardless of the handler.
+ if (const auto *DS = dyn_cast<DeclStmt>(St)) {
+ llvm::SmallPtrSet<const Stmt *, 4> HandledInits;
+ for (const Decl *D : DS->decls())
+ if (const auto *VD = dyn_cast<VarDecl>(D);
+ VD && VD->getInit() && !initializerIgnoresOverflowHandler(VD))
+ HandledInits.insert(VD->getInit());
+ for (const Stmt *Child : DS->children()) {
+ std::optional<OverflowHandlerScope> NoHandler;
+ if (!HandledInits.contains(Child))
+ NoHandler.emplace(Handlers, nullptr);
+ visit(Child);
+ }
+ return;
+ }
+
+ // A handler introduced inside a diagnosed operation (by a statement
+ // expression or the builtin) is a fresh context.
+ OverflowHandlerScope HandlerScope(Handlers, S.Context, St);
+ SaveAndRestore SaveInDiagnosedOp(
+ InDiagnosedOp, (InDiagnosedOp || Diagnosed) && !HandlerScope.pushed());
+ for (const Stmt *Child : St->children()) {
+ if (isOverflowHandlerUnevaluatedChild(S.Context, St, Child))
+ continue;
+ std::optional<OverflowHandlerScope> NoHandler;
+ if (childIgnoresOverflowHandler(St, Child))
+ NoHandler.emplace(Handlers, nullptr);
+ visit(Child);
+ }
+ }
+};
+} // namespace
+
+void Sema::DiagnoseOverflowHandlers(Decl *D, Stmt *Body) {
+ FunctionScopeInfo *FSI = getCurFunction();
+ if (!FSI || !Body || !getLangOpts().OverflowBehaviorTypes)
+ return;
+ // Templates are diagnosed per instantiation.
+ if (D->isTemplated())
+ return;
+ // A nested lambda or block is diagnosed with its enclosing function, whose
+ // handler labels are not all known yet.
+ if (FunctionScopes.size() > 1)
+ return;
----------------
Sirraide wrote:
Does this work with local classes? E.g.
```c++
void f() {
struct S {
void g() {
// Do we diagnose issues in here?
}
};
}
```
https://github.com/llvm/llvm-project/pull/235047
More information about the llvm-branch-commits
mailing list