aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td3
-rw-r--r--lib/Sema/SemaDecl.cpp54
-rw-r--r--test/CodeGen/attributes.c4
-rw-r--r--test/Sema/attr-noreturn.c2
4 files changed, 45 insertions, 18 deletions
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index ead116d8da..881e9d90a1 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1917,6 +1917,9 @@ def ext_return_has_void_expr : Extension<
def warn_noreturn_function_has_return_expr : Warning<
"function %0 declared 'noreturn' should not return">, DefaultError,
InGroup<DiagGroup<"invalid-noreturn">>;
+def warn_falloff_noreturn_function : Warning<
+ "function declared 'noreturn' should not return">, DefaultError,
+ InGroup<DiagGroup<"invalid-noreturn">>;
def err_noreturn_block_has_return_expr : Error<
"block declared 'noreturn' should not return">;
def err_block_on_nonlocal : Error<
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 2ecefde538..617943fd9f 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1015,6 +1015,13 @@ void Sema::MergeVarDecl(VarDecl *New, Decl *OldD) {
New->setPreviousDeclaration(Old);
}
+/// CheckFallThrough - Check that we don't fall off the end of a
+/// Statement that should return a value.
+///
+/// \returns AlwaysFallThrough iff we always fall off the end of the statement,
+/// MaybeFallThrough iff we might or might not fall off the end and
+/// NeverFallThrough iff we never fall off the end of the statement. We assume
+/// that functions not marked noreturn will return.
Sema::ControlFlowKind Sema::CheckFallThrough(Stmt *Root) {
llvm::OwningPtr<CFG> cfg (CFG::buildCFG(Root, &Context));
@@ -1101,12 +1108,9 @@ Sema::ControlFlowKind Sema::CheckFallThrough(Stmt *Root) {
}
/// CheckFallThroughForFunctionDef - Check that we don't fall off the end of a
-/// function that should return a value.
-///
-/// \returns AlwaysFallThrough iff we always fall off the end of the statement,
-/// MaybeFallThrough iff we might or might not fall off the end and
-/// NeverFallThrough iff we never fall off the end of the statement. We assume
-/// that functions not marked noreturn will return.
+/// function that should return a value. Check that we don't fall off the end
+/// of a noreturn function. We assume that functions not marked noreturn will
+/// return.
void Sema::CheckFallThroughForFunctionDef(Decl *D, Stmt *Body) {
// FIXME: Would be nice if we had a better way to control cascading errors,
// but for now, avoid them. The problem is that when Parse sees:
@@ -1116,17 +1120,39 @@ void Sema::CheckFallThroughForFunctionDef(Decl *D, Stmt *Body) {
// which this code would then warn about.
if (getDiagnostics().hasErrorOccurred())
return;
- if (Diags.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function)
- == Diagnostic::Ignored)
+ bool ReturnsVoid = false;
+ bool HasNoReturn = false;
+ if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ if (FD->getResultType()->isVoidType())
+ ReturnsVoid = true;
+ if (FD->hasAttr<NoReturnAttr>())
+ HasNoReturn = true;
+ } else if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ if (MD->getResultType()->isVoidType())
+ ReturnsVoid = true;
+ if (MD->hasAttr<NoReturnAttr>())
+ HasNoReturn = true;
+ }
+
+ if ((Diags.getDiagnosticLevel(diag::warn_maybe_falloff_nonvoid_function)
+ == Diagnostic::Ignored || ReturnsVoid)
+ && (Diags.getDiagnosticLevel(diag::warn_noreturn_function_has_return_expr)
+ == Diagnostic::Ignored || !HasNoReturn))
return;
// FIXME: Funtion try block
if (CompoundStmt *Compound = dyn_cast<CompoundStmt>(Body)) {
switch (CheckFallThrough(Body)) {
case MaybeFallThrough:
- Diag(Compound->getRBracLoc(), diag::warn_maybe_falloff_nonvoid_function);
+ if (HasNoReturn)
+ Diag(Compound->getRBracLoc(), diag::warn_falloff_noreturn_function);
+ else if (!ReturnsVoid)
+ Diag(Compound->getRBracLoc(),diag::warn_maybe_falloff_nonvoid_function);
break;
case AlwaysFallThrough:
- Diag(Compound->getRBracLoc(), diag::warn_falloff_nonvoid_function);
+ if (HasNoReturn)
+ Diag(Compound->getRBracLoc(), diag::warn_falloff_noreturn_function);
+ else if (!ReturnsVoid)
+ Diag(Compound->getRBracLoc(), diag::warn_falloff_nonvoid_function);
break;
case NeverFallThrough:
break;
@@ -3369,9 +3395,8 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,
Stmt *Body = BodyArg.takeAs<Stmt>();
if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(dcl)) {
FD->setBody(Body);
- if (!FD->getResultType()->isVoidType()
- // C and C++ allow for main to automagically return 0.
- && !FD->isMain())
+ if (!FD->isMain())
+ // C and C++ allow for main to automagically return 0.
CheckFallThroughForFunctionDef(FD, Body);
if (!FD->isInvalidDecl())
@@ -3387,8 +3412,7 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,
} else if (ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(dcl)) {
assert(MD == getCurMethodDecl() && "Method parsing confused");
MD->setBody(Body);
- if (!MD->getResultType()->isVoidType())
- CheckFallThroughForFunctionDef(MD, Body);
+ CheckFallThroughForFunctionDef(MD, Body);
MD->setEndLoc(Body->getLocEnd());
if (!MD->isInvalidDecl())
diff --git a/test/CodeGen/attributes.c b/test/CodeGen/attributes.c
index 8f157f3d31..00252b1135 100644
--- a/test/CodeGen/attributes.c
+++ b/test/CodeGen/attributes.c
@@ -17,7 +17,7 @@
// RUN: grep '@t16 = extern_weak global i32' %t &&
void t1() __attribute__((noreturn));
-void t1() {}
+void t1() { while (1) {} }
void t2() __attribute__((nothrow));
void t2() {}
@@ -33,7 +33,7 @@ int t5 __attribute__((weak)) = 2;
int t6 __attribute__((visibility("protected")));
void t7() __attribute__((noreturn, nothrow));
-void t7() {}
+void t7() { while (1) {} }
void __t8() {}
void t9() __attribute__((weak, alias("__t8")));
diff --git a/test/Sema/attr-noreturn.c b/test/Sema/attr-noreturn.c
index d1417f093f..2970a4827e 100644
--- a/test/Sema/attr-noreturn.c
+++ b/test/Sema/attr-noreturn.c
@@ -4,7 +4,7 @@ static void (*fp0)(void) __attribute__((noreturn));
static void __attribute__((noreturn)) f0(void) {
fatal();
-}
+} // expected-error {{function declared 'noreturn' should not return}}
// On K&R
int f1() __attribute__((noreturn));