From: Mike Stump Date: Fri, 24 Jul 2009 02:49:01 +0000 (+0000) Subject: Implement new warning for functions declared 'noreturn' when they fall off the end. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=5f28a1edf6decac64ca12e51ca4a929d26fc21f2;p=clang Implement new warning for functions declared 'noreturn' when they fall off the end. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@76932 91177308-0d34-0410-b5e6-96231b3b80d8 --- 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>; +def warn_falloff_noreturn_function : Warning< + "function declared 'noreturn' should not return">, DefaultError, + InGroup>; 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::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(D)) { + if (FD->getResultType()->isVoidType()) + ReturnsVoid = true; + if (FD->hasAttr()) + HasNoReturn = true; + } else if (ObjCMethodDecl *MD = dyn_cast(D)) { + if (MD->getResultType()->isVoidType()) + ReturnsVoid = true; + if (MD->hasAttr()) + 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(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(); if (FunctionDecl *FD = dyn_cast_or_null(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(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));