| 
40 | 40 | #include "clang/Sema/ParsedAttr.h"  | 
41 | 41 | #include "clang/Sema/Scope.h"  | 
42 | 42 | #include "clang/Sema/ScopeInfo.h"  | 
 | 43 | +#include "clang/Sema/Sema.h"  | 
43 | 44 | #include "clang/Sema/SemaAMDGPU.h"  | 
44 | 45 | #include "clang/Sema/SemaARM.h"  | 
45 | 46 | #include "clang/Sema/SemaAVR.h"  | 
@@ -1938,6 +1939,49 @@ static void handleNakedAttr(Sema &S, Decl *D, const ParsedAttr &AL) {  | 
1938 | 1939 |   D->addAttr(::new (S.Context) NakedAttr(S.Context, AL));  | 
1939 | 1940 | }  | 
1940 | 1941 | 
 
  | 
 | 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) {  | 
 | 1947 | +  if (!FD->hasBody())  | 
 | 1948 | +    return false;  | 
 | 1949 | +  const Stmt *Body = FD->getBody();  | 
 | 1950 | +  const Stmt *OnlyStmt = nullptr;  | 
 | 1951 | + | 
 | 1952 | +  if (const auto *Compound = dyn_cast<CompoundStmt>(Body)) {  | 
 | 1953 | +    if (Compound->size() != 1)  | 
 | 1954 | +      return false; // More than one statement, can't be known to always throw.  | 
 | 1955 | +    OnlyStmt = *Compound->body_begin();  | 
 | 1956 | +  } else {  | 
 | 1957 | +    OnlyStmt = Body;  | 
 | 1958 | +  }  | 
 | 1959 | + | 
 | 1960 | +  // Unwrap ExprWithCleanups if necessary.  | 
 | 1961 | +  if (const auto *EWC = dyn_cast<ExprWithCleanups>(OnlyStmt)) {  | 
 | 1962 | +    OnlyStmt = EWC->getSubExpr();  | 
 | 1963 | +  }  | 
 | 1964 | +  // Check if the only statement is a throw expression.  | 
 | 1965 | +  return isa<CXXThrowExpr>(OnlyStmt);  | 
 | 1966 | +}  | 
 | 1967 | + | 
 | 1968 | +void clang::inferNoReturnAttr(Sema &S, const Decl *D) {  | 
 | 1969 | +  auto *FD = dyn_cast<FunctionDecl>(D);  | 
 | 1970 | +  if (!FD)  | 
 | 1971 | +    return;  | 
 | 1972 | + | 
 | 1973 | +  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;  | 
 | 1978 | + | 
 | 1979 | +  if (!FD->hasAttr<NoReturnAttr>() && !FD->hasAttr<InferredNoReturnAttr>() &&  | 
 | 1980 | +      isKnownToAlwaysThrow(FD)) {  | 
 | 1981 | +    NonConstFD->addAttr(InferredNoReturnAttr::CreateImplicit(S.Context));  | 
 | 1982 | +  }  | 
 | 1983 | +}  | 
 | 1984 | + | 
1941 | 1985 | static void handleNoReturnAttr(Sema &S, Decl *D, const ParsedAttr &Attrs) {  | 
1942 | 1986 |   if (hasDeclarator(D)) return;  | 
1943 | 1987 | 
 
  | 
 | 
0 commit comments