xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp (revision e5dd70708596ae51455a0ffa086a00c5b29f8583)
1*e5dd7070Spatrick //=- NSAutoreleasePoolChecker.cpp --------------------------------*- C++ -*-==//
2*e5dd7070Spatrick //
3*e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5*e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*e5dd7070Spatrick //
7*e5dd7070Spatrick //===----------------------------------------------------------------------===//
8*e5dd7070Spatrick //
9*e5dd7070Spatrick //  This file defines a NSAutoreleasePoolChecker, a small checker that warns
10*e5dd7070Spatrick //  about subpar uses of NSAutoreleasePool.  Note that while the check itself
11*e5dd7070Spatrick //  (in its current form) could be written as a flow-insensitive check, in
12*e5dd7070Spatrick //  can be potentially enhanced in the future with flow-sensitive information.
13*e5dd7070Spatrick //  It is also a good example of the CheckerVisitor interface.
14*e5dd7070Spatrick //
15*e5dd7070Spatrick //===----------------------------------------------------------------------===//
16*e5dd7070Spatrick 
17*e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18*e5dd7070Spatrick #include "clang/AST/Decl.h"
19*e5dd7070Spatrick #include "clang/AST/DeclObjC.h"
20*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
21*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
23*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h"
24*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
25*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
26*e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
27*e5dd7070Spatrick 
28*e5dd7070Spatrick using namespace clang;
29*e5dd7070Spatrick using namespace ento;
30*e5dd7070Spatrick 
31*e5dd7070Spatrick namespace {
32*e5dd7070Spatrick class NSAutoreleasePoolChecker
33*e5dd7070Spatrick   : public Checker<check::PreObjCMessage> {
34*e5dd7070Spatrick   mutable std::unique_ptr<BugType> BT;
35*e5dd7070Spatrick   mutable Selector releaseS;
36*e5dd7070Spatrick 
37*e5dd7070Spatrick public:
38*e5dd7070Spatrick   void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
39*e5dd7070Spatrick };
40*e5dd7070Spatrick 
41*e5dd7070Spatrick } // end anonymous namespace
42*e5dd7070Spatrick 
43*e5dd7070Spatrick void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
44*e5dd7070Spatrick                                                    CheckerContext &C) const {
45*e5dd7070Spatrick   if (!msg.isInstanceMessage())
46*e5dd7070Spatrick     return;
47*e5dd7070Spatrick 
48*e5dd7070Spatrick   const ObjCInterfaceDecl *OD = msg.getReceiverInterface();
49*e5dd7070Spatrick   if (!OD)
50*e5dd7070Spatrick     return;
51*e5dd7070Spatrick   if (!OD->getIdentifier()->isStr("NSAutoreleasePool"))
52*e5dd7070Spatrick     return;
53*e5dd7070Spatrick 
54*e5dd7070Spatrick   if (releaseS.isNull())
55*e5dd7070Spatrick     releaseS = GetNullarySelector("release", C.getASTContext());
56*e5dd7070Spatrick   // Sending 'release' message?
57*e5dd7070Spatrick   if (msg.getSelector() != releaseS)
58*e5dd7070Spatrick     return;
59*e5dd7070Spatrick 
60*e5dd7070Spatrick   if (!BT)
61*e5dd7070Spatrick     BT.reset(new BugType(this, "Use -drain instead of -release",
62*e5dd7070Spatrick                          "API Upgrade (Apple)"));
63*e5dd7070Spatrick 
64*e5dd7070Spatrick   ExplodedNode *N = C.generateNonFatalErrorNode();
65*e5dd7070Spatrick   if (!N) {
66*e5dd7070Spatrick     assert(0);
67*e5dd7070Spatrick     return;
68*e5dd7070Spatrick   }
69*e5dd7070Spatrick 
70*e5dd7070Spatrick   auto Report = std::make_unique<PathSensitiveBugReport>(
71*e5dd7070Spatrick       *BT,
72*e5dd7070Spatrick       "Use -drain instead of -release when using NSAutoreleasePool and "
73*e5dd7070Spatrick       "garbage collection",
74*e5dd7070Spatrick       N);
75*e5dd7070Spatrick   Report->addRange(msg.getSourceRange());
76*e5dd7070Spatrick   C.emitReport(std::move(Report));
77*e5dd7070Spatrick }
78*e5dd7070Spatrick 
79*e5dd7070Spatrick void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) {
80*e5dd7070Spatrick   mgr.registerChecker<NSAutoreleasePoolChecker>();
81*e5dd7070Spatrick }
82*e5dd7070Spatrick 
83*e5dd7070Spatrick bool ento::shouldRegisterNSAutoreleasePoolChecker(const LangOptions &LO) {
84*e5dd7070Spatrick   return LO.getGC() != LangOptions::NonGC;
85*e5dd7070Spatrick }
86