Skip to content

Commit 602b472

Browse files
committed
Suppress noreturn warning if last statement in a function is a throw
1 parent ec48d15 commit 602b472

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

clang/lib/Sema/AnalysisBasedWarnings.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -fexceptions -Wreturn-type -verify %s
2+
// expected-no-diagnostics
3+
4+
namespace std {
5+
class string {
6+
public:
7+
string(const char*); // constructor for runtime_error
8+
};
9+
class runtime_error {
10+
public:
11+
runtime_error(const string &);
12+
};
13+
}
14+
15+
void throwError(const std::string& msg) {
16+
throw std::runtime_error(msg);
17+
}
18+
19+
int ensureZero(const int i) {
20+
if (i == 0) return 0;
21+
throwError("ERROR"); // no-warning
22+
}

0 commit comments

Comments
 (0)