1*d99bd55aSTed Kremenek //=- NSAutoreleasePoolChecker.cpp --------------------------------*- C++ -*-==//
2*d99bd55aSTed Kremenek //
3*d99bd55aSTed Kremenek //                     The LLVM Compiler Infrastructure
4*d99bd55aSTed Kremenek //
5*d99bd55aSTed Kremenek // This file is distributed under the University of Illinois Open Source
6*d99bd55aSTed Kremenek // License. See LICENSE.TXT for details.
7*d99bd55aSTed Kremenek //
8*d99bd55aSTed Kremenek //===----------------------------------------------------------------------===//
9*d99bd55aSTed Kremenek //
10*d99bd55aSTed Kremenek //  This file defines a NSAutoreleasePoolChecker, a small checker that warns
11*d99bd55aSTed Kremenek //  about subpar uses of NSAutoreleasePool.  Note that while the check itself
12*d99bd55aSTed Kremenek //  (in it's current form) could be written as a flow-insensitive check, in
13*d99bd55aSTed Kremenek //  can be potentially enhanced in the future with flow-sensitive information.
14*d99bd55aSTed Kremenek //  It is also a good example of the CheckerVisitor interface.
15*d99bd55aSTed Kremenek //
16*d99bd55aSTed Kremenek //===----------------------------------------------------------------------===//
17*d99bd55aSTed Kremenek 
18*d99bd55aSTed Kremenek #include "clang/StaticAnalyzer/BugReporter/BugReporter.h"
19*d99bd55aSTed Kremenek #include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h"
20*d99bd55aSTed Kremenek #include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h"
21*d99bd55aSTed Kremenek #include "BasicObjCFoundationChecks.h"
22*d99bd55aSTed Kremenek #include "clang/AST/DeclObjC.h"
23*d99bd55aSTed Kremenek #include "clang/AST/Decl.h"
24*d99bd55aSTed Kremenek 
25*d99bd55aSTed Kremenek using namespace clang;
26*d99bd55aSTed Kremenek using namespace ento;
27*d99bd55aSTed Kremenek 
28*d99bd55aSTed Kremenek namespace {
29*d99bd55aSTed Kremenek class NSAutoreleasePoolChecker
30*d99bd55aSTed Kremenek   : public CheckerVisitor<NSAutoreleasePoolChecker> {
31*d99bd55aSTed Kremenek 
32*d99bd55aSTed Kremenek   Selector releaseS;
33*d99bd55aSTed Kremenek 
34*d99bd55aSTed Kremenek public:
35*d99bd55aSTed Kremenek     NSAutoreleasePoolChecker(Selector release_s) : releaseS(release_s) {}
36*d99bd55aSTed Kremenek 
37*d99bd55aSTed Kremenek   static void *getTag() {
38*d99bd55aSTed Kremenek     static int x = 0;
39*d99bd55aSTed Kremenek     return &x;
40*d99bd55aSTed Kremenek   }
41*d99bd55aSTed Kremenek 
42*d99bd55aSTed Kremenek   void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
43*d99bd55aSTed Kremenek };
44*d99bd55aSTed Kremenek 
45*d99bd55aSTed Kremenek } // end anonymous namespace
46*d99bd55aSTed Kremenek 
47*d99bd55aSTed Kremenek 
48*d99bd55aSTed Kremenek void ento::RegisterNSAutoreleasePoolChecks(ExprEngine &Eng) {
49*d99bd55aSTed Kremenek   ASTContext &Ctx = Eng.getContext();
50*d99bd55aSTed Kremenek   if (Ctx.getLangOptions().getGCMode() != LangOptions::NonGC) {
51*d99bd55aSTed Kremenek     Eng.registerCheck(new NSAutoreleasePoolChecker(GetNullarySelector("release",
52*d99bd55aSTed Kremenek                                                                       Ctx)));
53*d99bd55aSTed Kremenek   }
54*d99bd55aSTed Kremenek }
55*d99bd55aSTed Kremenek 
56*d99bd55aSTed Kremenek void
57*d99bd55aSTed Kremenek NSAutoreleasePoolChecker::PreVisitObjCMessageExpr(CheckerContext &C,
58*d99bd55aSTed Kremenek                                                   const ObjCMessageExpr *ME) {
59*d99bd55aSTed Kremenek 
60*d99bd55aSTed Kremenek   const Expr *receiver = ME->getInstanceReceiver();
61*d99bd55aSTed Kremenek   if (!receiver)
62*d99bd55aSTed Kremenek     return;
63*d99bd55aSTed Kremenek 
64*d99bd55aSTed Kremenek   // FIXME: Enhance with value-tracking information instead of consulting
65*d99bd55aSTed Kremenek   // the type of the expression.
66*d99bd55aSTed Kremenek   const ObjCObjectPointerType* PT =
67*d99bd55aSTed Kremenek     receiver->getType()->getAs<ObjCObjectPointerType>();
68*d99bd55aSTed Kremenek 
69*d99bd55aSTed Kremenek   if (!PT)
70*d99bd55aSTed Kremenek     return;
71*d99bd55aSTed Kremenek   const ObjCInterfaceDecl* OD = PT->getInterfaceDecl();
72*d99bd55aSTed Kremenek   if (!OD)
73*d99bd55aSTed Kremenek     return;
74*d99bd55aSTed Kremenek   if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool"))
75*d99bd55aSTed Kremenek     return;
76*d99bd55aSTed Kremenek 
77*d99bd55aSTed Kremenek   // Sending 'release' message?
78*d99bd55aSTed Kremenek   if (ME->getSelector() != releaseS)
79*d99bd55aSTed Kremenek     return;
80*d99bd55aSTed Kremenek 
81*d99bd55aSTed Kremenek   SourceRange R = ME->getSourceRange();
82*d99bd55aSTed Kremenek 
83*d99bd55aSTed Kremenek   C.getBugReporter().EmitBasicReport("Use -drain instead of -release",
84*d99bd55aSTed Kremenek     "API Upgrade (Apple)",
85*d99bd55aSTed Kremenek     "Use -drain instead of -release when using NSAutoreleasePool "
86*d99bd55aSTed Kremenek     "and garbage collection", ME->getLocStart(), &R, 1);
87*d99bd55aSTed Kremenek }
88