2323#include " clang/AST/ExprCXX.h"
2424#include " clang/AST/Mangle.h"
2525#include " clang/AST/Type.h"
26+ #include " clang/Analysis/AnalysisDeclContext.h"
2627#include " clang/Basic/CharInfo.h"
2728#include " clang/Basic/Cuda.h"
2829#include " clang/Basic/DarwinSDKInfo.h"
3233#include " clang/Basic/SourceManager.h"
3334#include " clang/Basic/TargetInfo.h"
3435#include " clang/Lex/Preprocessor.h"
36+ #include " clang/Sema/AnalysisBasedWarnings.h"
3537#include " clang/Sema/Attr.h"
3638#include " clang/Sema/DeclSpec.h"
3739#include " clang/Sema/DelayedDiagnostic.h"
@@ -1939,11 +1941,7 @@ static void handleNakedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
19391941 D->addAttr (::new (S.Context ) NakedAttr (S.Context , AL));
19401942}
19411943
1942- // FIXME: This is a best-effort heuristic.
1943- // Currently only handles single throw expressions (optionally with
1944- // ExprWithCleanups). We could expand this to perform control-flow analysis for
1945- // more complex patterns.
1946- static bool isKnownToAlwaysThrow (const FunctionDecl *FD) {
1944+ static bool alwaysThrowsDirect (const FunctionDecl *FD) {
19471945 if (!FD->hasBody ())
19481946 return false ;
19491947 const Stmt *Body = FD->getBody ();
@@ -1965,25 +1963,52 @@ static bool isKnownToAlwaysThrow(const FunctionDecl *FD) {
19651963 return isa<CXXThrowExpr>(OnlyStmt);
19661964}
19671965
1968- void clang::inferNoReturnAttr (Sema &S, const Decl *D) {
1966+ static bool isKnownToAlwaysThrowCFG (const FunctionDecl *FD, Sema &S) {
1967+ if (!FD->hasBody ())
1968+ return false ;
1969+
1970+ // Drop the const qualifier to do an analysis.
1971+ FunctionDecl *NonConstFD = const_cast <FunctionDecl *>(FD);
1972+ AnalysisDeclContext AC (nullptr , NonConstFD);
1973+
1974+ switch (CheckFallThrough (AC)) {
1975+ case NeverFallThrough:
1976+ case NeverFallThroughOrReturn:
1977+ return true ;
1978+ case AlwaysFallThrough:
1979+ case UnknownFallThrough:
1980+ case MaybeFallThrough:
1981+ return false ;
1982+ }
1983+ }
1984+
1985+ bool clang::inferNoReturnAttr (Sema &S, const Decl *D, bool FirstPass) {
19691986 auto *FD = dyn_cast<FunctionDecl>(D);
19701987 if (!FD)
1971- return ;
1988+ return false ;
1989+ if (FD->isInvalidDecl ())
1990+ return false ;
1991+ if (FD->hasAttr <NoReturnAttr>() || FD->hasAttr <InferredNoReturnAttr>())
1992+ return false ;
19721993
19731994 auto *NonConstFD = const_cast <FunctionDecl *>(FD);
1974- DiagnosticsEngine &Diags = S.getDiagnostics ();
1975- if (Diags.isIgnored (diag::warn_falloff_nonvoid, FD->getLocation ()) &&
1976- Diags.isIgnored (diag::warn_suggest_noreturn_function, FD->getLocation ()))
1977- return ;
19781995
1979- if (!FD->hasAttr <NoReturnAttr>() && !FD->hasAttr <InferredNoReturnAttr>() &&
1980- isKnownToAlwaysThrow (FD)) {
1981- NonConstFD->addAttr (InferredNoReturnAttr::CreateImplicit (S.Context ));
1996+ auto tryAddInferredNoReturn = [&](bool Condition) {
1997+ if (Condition) {
1998+ NonConstFD->addAttr (InferredNoReturnAttr::CreateImplicit (S.Context ));
1999+ if (FD->getReturnType ()->isVoidType ())
2000+ S.Diag (FD->getLocation (), diag::warn_suggest_noreturn_function)
2001+ << /* isFunction=*/ 0 << FD;
2002+ return true ;
2003+ }
2004+ return false ;
2005+ };
19822006
1983- // Emit a diagnostic suggesting the function being marked [[noreturn]].
1984- S. Diag (FD-> getLocation (), diag::warn_suggest_noreturn_function)
1985- << /* isFunction= */ 0 << FD ;
2007+ if (FirstPass) {
2008+ // Look for trivial function bodies that always throw.
2009+ return tryAddInferredNoReturn ( alwaysThrowsDirect (FD)) ;
19862010 }
2011+ return tryAddInferredNoReturn (isKnownToAlwaysThrowCFG (FD, S));
19872012}
19882013
19892014static void handleNoReturnAttr (Sema &S, Decl *D, const ParsedAttr &Attrs) {
0 commit comments