diff --git a/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -44,9 +44,11 @@ if (const FunctionDecl *FD = dyn_cast_or_null(CE.getDecl())) BuildSinks = FD->hasAttr() || FD->isNoReturn(); - const Expr *Callee = CE.getOriginExpr(); - if (!BuildSinks && Callee) - BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); + if (const CallExpr *CExpr = dyn_cast_or_null(CE.getOriginExpr()); + CExpr && !BuildSinks) { + if (const Expr *C = CExpr->getCallee()) + BuildSinks = getFunctionExtInfo(C->getType()).getNoReturn(); + } if (!BuildSinks && CE.isGlobalCFunction()) { if (const IdentifierInfo *II = CE.getCalleeIdentifier()) { diff --git a/clang/test/Analysis/no-return.c b/clang/test/Analysis/no-return.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/no-return.c @@ -0,0 +1,36 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s + +typedef void(fatal_fun)() __attribute__((__noreturn__)); +fatal_fun* fatal_fptr; +void fatal_decl() __attribute__((__noreturn__)); + +int rng(); + +/// This code calls a [[noreturn]] function pointer, which used to be handled +/// inconsistently between AST builder and CSA. +/// In the result, CSA produces a path where this function returns non-0. +int return_zero_or_abort_by_fnptr() { + if (rng()) fatal_fptr(); + return 0; +} + +/// This function calls a [[noreturn]] function. +/// If it does return, it always returns 0. +int return_zero_or_abort_by_direct_fun() { + if (rng()) fatal_decl(); + return 0; +} + +/// Trigger a division by zero issue depending on the return value +/// of the called functions. +int caller() { + int x = 0; + // The following if branches must never be taken. + if (return_zero_or_abort_by_fnptr()) + return 1 / x; // no-warning: Dead code. + if (return_zero_or_abort_by_direct_fun()) + return 1 / x; // no-warning: Dead code. + + // Make sure the warning is still reported when viable. + return 1 / x; // expected-warning {{Division by zero}} +}