@@ -626,6 +626,31 @@ struct CheckFallThroughDiagnostics {
626626
627627} // anonymous namespace
628628
629+ static bool isKnownToAlwaysThrow (const FunctionDecl *FD) {
630+ if (!FD->hasBody ())
631+ return false ;
632+ const Stmt *Body = FD->getBody ();
633+ const Stmt *OnlyStmt = nullptr ;
634+
635+ if (const auto *Compound = dyn_cast<CompoundStmt>(Body)) {
636+ if (Compound->size () != 1 )
637+ return false ; // More than one statement, can't be known to always throw.
638+ OnlyStmt = *Compound->body_begin ();
639+ } else {
640+ OnlyStmt = Body;
641+ }
642+
643+ // Unwrap ExprWithCleanups if necessary.
644+ if (const auto *EWC = dyn_cast<ExprWithCleanups>(OnlyStmt)) {
645+ OnlyStmt = EWC->getSubExpr ();
646+ }
647+ // Check if the only statement is a throw expression.
648+ if (isa<CXXThrowExpr>(OnlyStmt)) {
649+ return true ; // Known to always throw.
650+ }
651+ return false ; // Not known to always throw.
652+ }
653+
629654// / CheckFallThroughForBody - Check that we don't fall off the end of a
630655// / function that should return a value. Check that we don't fall off the end
631656// / of a noreturn function. We assume that functions and blocks not marked
@@ -681,6 +706,26 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body,
681706 if (CD.diag_FallThrough_HasNoReturn )
682707 S.Diag (RBrace, CD.diag_FallThrough_HasNoReturn ) << CD.FunKind ;
683708 } else if (!ReturnsVoid && CD.diag_FallThrough_ReturnsNonVoid ) {
709+ // If the final statement is a call to an always-throwing function,
710+ // don't warn about the fall-through.
711+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
712+ if (const auto *CS = dyn_cast<CompoundStmt>(Body)) {
713+ if (!CS->body_empty ()) {
714+ const Stmt *LastStmt = CS->body_back ();
715+ // Unwrap ExprWithCleanups if necessary.
716+ if (const auto *EWC = dyn_cast<ExprWithCleanups>(LastStmt)) {
717+ LastStmt = EWC->getSubExpr ();
718+ }
719+ if (const auto *CE = dyn_cast<CallExpr>(LastStmt)) {
720+ if (const FunctionDecl *Callee = CE->getDirectCallee ()) {
721+ if (isKnownToAlwaysThrow (Callee)) {
722+ return ; // Don't warn about fall-through.
723+ }
724+ }
725+ }
726+ }
727+ }
728+ }
684729 bool NotInAllControlPaths = FallThroughType == MaybeFallThrough;
685730 S.Diag (RBrace, CD.diag_FallThrough_ReturnsNonVoid )
686731 << CD.FunKind << NotInAllControlPaths;
0 commit comments