NoReturnFunctionChecker.cpp revision 226633
1295958Ssephe//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===//
2298446Ssephe//
3295958Ssephe//                     The LLVM Compiler Infrastructure
4295958Ssephe//
5295958Ssephe// This file is distributed under the University of Illinois Open Source
6295958Ssephe// License. See LICENSE.TXT for details.
7295958Ssephe//
8295958Ssephe//===----------------------------------------------------------------------===//
9295958Ssephe//
10295958Ssephe// This defines NoReturnFunctionChecker, which evaluates functions that do not
11295958Ssephe// return to the caller.
12295958Ssephe//
13295958Ssephe//===----------------------------------------------------------------------===//
14295958Ssephe
15295958Ssephe#include "ClangSACheckers.h"
16295958Ssephe#include "clang/StaticAnalyzer/Core/Checker.h"
17295958Ssephe#include "clang/StaticAnalyzer/Core/CheckerManager.h"
18295958Ssephe#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19295958Ssephe#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
20295958Ssephe#include "llvm/ADT/StringSwitch.h"
21295958Ssephe#include <cstdarg>
22295958Ssephe
23295958Ssepheusing namespace clang;
24295958Ssepheusing namespace ento;
25295958Ssephe
26295958Ssephenamespace {
27295958Ssephe
28295958Ssepheclass NoReturnFunctionChecker : public Checker< check::PostStmt<CallExpr>,
29295958Ssephe                                                check::PostObjCMessage > {
30295958Ssephepublic:
31295958Ssephe  void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
32295958Ssephe  void checkPostObjCMessage(const ObjCMessage &msg, CheckerContext &C) const;
33295958Ssephe};
34295958Ssephe
35295958Ssephe}
36295958Ssephe
37295958Ssephevoid NoReturnFunctionChecker::checkPostStmt(const CallExpr *CE,
38302880Ssephe                                            CheckerContext &C) const {
39303067Ssephe  const ProgramState *state = C.getState();
40295958Ssephe  const Expr *Callee = CE->getCallee();
41302698Ssephe
42295958Ssephe  bool BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn();
43295958Ssephe
44302802Ssephe  if (!BuildSinks) {
45295958Ssephe    SVal L = state->getSVal(Callee);
46295958Ssephe    const FunctionDecl *FD = L.getAsFunctionDecl();
47295958Ssephe    if (!FD)
48295958Ssephe      return;
49295958Ssephe
50295958Ssephe    if (FD->getAttr<AnalyzerNoReturnAttr>())
51295958Ssephe      BuildSinks = true;
52295958Ssephe    else if (const IdentifierInfo *II = FD->getIdentifier()) {
53295958Ssephe      // HACK: Some functions are not marked noreturn, and don't return.
54295958Ssephe      //  Here are a few hardwired ones.  If this takes too long, we can
55295958Ssephe      //  potentially cache these results.
56302885Ssephe      BuildSinks
57295958Ssephe        = llvm::StringSwitch<bool>(StringRef(II->getName()))
58295958Ssephe            .Case("exit", true)
59295958Ssephe            .Case("panic", true)
60295958Ssephe            .Case("error", true)
61295958Ssephe            .Case("Assert", true)
62295958Ssephe            // FIXME: This is just a wrapper around throwing an exception.
63295958Ssephe            //  Eventually inter-procedural analysis should handle this easily.
64295958Ssephe            .Case("ziperr", true)
65297793Spfg            .Case("assfail", true)
66302702Ssephe            .Case("db_error", true)
67295958Ssephe            .Case("__assert", true)
68302885Ssephe            .Case("__assert_rtn", true)
69302885Ssephe            .Case("__assert_fail", true)
70302885Ssephe            .Case("dtrace_assfail", true)
71302885Ssephe            .Case("yy_fatal_error", true)
72295958Ssephe            .Case("_XCAssertionFailureHandler", true)
73295958Ssephe            .Case("_DTAssertionFailureHandler", true)
74295958Ssephe            .Case("_TSAssertionFailureHandler", true)
75295958Ssephe            .Default(false);
76295958Ssephe    }
77295958Ssephe  }
78295958Ssephe
79295958Ssephe  if (BuildSinks)
80295958Ssephe    C.generateSink();
81295958Ssephe}
82295958Ssephe
83295958Ssephestatic bool END_WITH_NULL isMultiArgSelector(const Selector *Sel, ...) {
84295958Ssephe  va_list argp;
85295958Ssephe  va_start(argp, Sel);
86295958Ssephe
87295958Ssephe  unsigned Slot = 0;
88295958Ssephe  const char *Arg;
89295958Ssephe  while ((Arg = va_arg(argp, const char *))) {
90295958Ssephe    if (!Sel->getNameForSlot(Slot).equals(Arg))
91295958Ssephe      break; // still need to va_end!
92295958Ssephe    ++Slot;
93302882Ssephe  }
94302882Ssephe
95295958Ssephe  va_end(argp);
96295958Ssephe
97295958Ssephe  // We only succeeded if we made it to the end of the argument list.
98295958Ssephe  return (Arg == NULL);
99295958Ssephe}
100295958Ssephe
101297220Ssephevoid NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMessage &Msg,
102297220Ssephe                                                   CheckerContext &C) const {
103297220Ssephe  // HACK: This entire check is to handle two messages in the Cocoa frameworks:
104302698Ssephe  // -[NSAssertionHandler
105295958Ssephe  //    handleFailureInMethod:object:file:lineNumber:description:]
106295958Ssephe  // -[NSAssertionHandler
107295958Ssephe  //    handleFailureInFunction:file:lineNumber:description:]
108295958Ssephe  // Eventually these should be annotated with __attribute__((noreturn)).
109295958Ssephe  // Because ObjC messages use dynamic dispatch, it is not generally safe to
110295958Ssephe  // assume certain methods can't return. In cases where it is definitely valid,
111295958Ssephe  // see if you can mark the methods noreturn or analyzer_noreturn instead of
112295958Ssephe  // adding more explicit checks to this method.
113295958Ssephe
114295958Ssephe  if (!Msg.isInstanceMessage())
115295958Ssephe    return;
116295958Ssephe
117295958Ssephe  const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface();
118295958Ssephe  if (!Receiver)
119295958Ssephe    return;
120295958Ssephe  if (!Receiver->getIdentifier()->isStr("NSAssertionHandler"))
121295958Ssephe    return;
122295958Ssephe
123295958Ssephe  Selector Sel = Msg.getSelector();
124295958Ssephe  switch (Sel.getNumArgs()) {
125295958Ssephe  default:
126295958Ssephe    return;
127295958Ssephe  case 4:
128295958Ssephe    if (!isMultiArgSelector(&Sel, "handleFailureInFunction", "file",
129295958Ssephe                            "lineNumber", "description", NULL))
130295958Ssephe      return;
131295958Ssephe    break;
132295958Ssephe  case 5:
133295958Ssephe    if (!isMultiArgSelector(&Sel, "handleFailureInMethod", "object", "file",
134295958Ssephe                            "lineNumber", "description", NULL))
135295958Ssephe      return;
136    break;
137  }
138
139  // If we got here, it's one of the messages we care about.
140  C.generateSink();
141}
142
143
144void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) {
145  mgr.registerChecker<NoReturnFunctionChecker>();
146}
147