blob: adae7447c5f95b072967ce7e97f77b1dd36c0eff [file] [log] [blame]
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +00001//=- NSErrorChecker.cpp - Coding conventions for uses of NSError -*- C++ -*-==//
Ted Kremenekfc3abeb2008-09-18 06:33:41 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines a CheckNSError, a flow-insenstive check
11// that determines if an Objective-C class interface correctly returns
12// a non-void return type.
13//
14// File under feature request PR 2600.
15//
16//===----------------------------------------------------------------------===//
17
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000018#include "ClangSACheckers.h"
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000019#include "clang/StaticAnalyzer/Core/Checker.h"
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000020#include "clang/StaticAnalyzer/Core/CheckerManager.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Ted Kremenek001fd5b2011-08-15 22:09:50 +000022#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
Ted Kremenekf8cbac42011-02-10 01:03:03 +000023#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
Ted Kremenekfc3abeb2008-09-18 06:33:41 +000024#include "clang/AST/DeclObjC.h"
Ted Kremenekf0673e42008-09-18 21:25:13 +000025#include "clang/AST/Decl.h"
26#include "llvm/ADT/SmallVector.h"
Ted Kremenekfc3abeb2008-09-18 06:33:41 +000027
28using namespace clang;
Ted Kremenek98857c92010-12-23 07:20:52 +000029using namespace ento;
Ted Kremenekfc3abeb2008-09-18 06:33:41 +000030
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +000031static bool IsNSError(QualType T, IdentifierInfo *II);
32static bool IsCFError(QualType T, IdentifierInfo *II);
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000033
34//===----------------------------------------------------------------------===//
35// NSErrorMethodChecker
36//===----------------------------------------------------------------------===//
37
Ted Kremenekf0673e42008-09-18 21:25:13 +000038namespace {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000039class NSErrorMethodChecker
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000040 : public Checker< check::ASTDecl<ObjCMethodDecl> > {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000041 mutable IdentifierInfo *II;
Mike Stump11289f42009-09-09 15:08:12 +000042
Ted Kremenekf0673e42008-09-18 21:25:13 +000043public:
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000044 NSErrorMethodChecker() : II(0) { }
Mike Stump11289f42009-09-09 15:08:12 +000045
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000046 void checkASTDecl(const ObjCMethodDecl *D,
47 AnalysisManager &mgr, BugReporter &BR) const;
48};
49}
50
51void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,
52 AnalysisManager &mgr,
53 BugReporter &BR) const {
54 if (!D->isThisDeclarationADefinition())
55 return;
56 if (!D->getResultType()->isVoidType())
57 return;
58
59 if (!II)
60 II = &D->getASTContext().Idents.get("NSError");
61
62 bool hasNSError = false;
63 for (ObjCMethodDecl::param_iterator
64 I = D->param_begin(), E = D->param_end(); I != E; ++I) {
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +000065 if (IsNSError((*I)->getType(), II)) {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000066 hasNSError = true;
67 break;
68 }
69 }
70
71 if (hasNSError) {
72 const char *err = "Method accepting NSError** "
73 "should have a non-void return value to indicate whether or not an "
74 "error occurred";
75 BR.EmitBasicReport("Bad return type when passing NSError**",
76 "Coding conventions (Apple)", err, D->getLocation());
77 }
78}
79
80//===----------------------------------------------------------------------===//
81// CFErrorFunctionChecker
82//===----------------------------------------------------------------------===//
83
84namespace {
85class CFErrorFunctionChecker
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000086 : public Checker< check::ASTDecl<FunctionDecl> > {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +000087 mutable IdentifierInfo *II;
88
89public:
90 CFErrorFunctionChecker() : II(0) { }
91
92 void checkASTDecl(const FunctionDecl *D,
93 AnalysisManager &mgr, BugReporter &BR) const;
94};
95}
96
97void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
98 AnalysisManager &mgr,
99 BugReporter &BR) const {
Alexis Hunt4a8ea102011-05-06 20:44:56 +0000100 if (!D->doesThisDeclarationHaveABody())
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000101 return;
102 if (!D->getResultType()->isVoidType())
103 return;
104
105 if (!II)
106 II = &D->getASTContext().Idents.get("CFErrorRef");
107
108 bool hasCFError = false;
109 for (FunctionDecl::param_const_iterator
110 I = D->param_begin(), E = D->param_end(); I != E; ++I) {
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000111 if (IsCFError((*I)->getType(), II)) {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000112 hasCFError = true;
113 break;
114 }
115 }
116
117 if (hasCFError) {
118 const char *err = "Function accepting CFErrorRef* "
119 "should have a non-void return value to indicate whether or not an "
120 "error occurred";
121 BR.EmitBasicReport("Bad return type when passing CFErrorRef*",
122 "Coding conventions (Apple)", err, D->getLocation());
123 }
124}
125
126//===----------------------------------------------------------------------===//
127// NSOrCFErrorDerefChecker
128//===----------------------------------------------------------------------===//
129
130namespace {
131
132class NSErrorDerefBug : public BugType {
133public:
134 NSErrorDerefBug() : BugType("NSError** null dereference",
135 "Coding conventions (Apple)") {}
Mike Stump11289f42009-09-09 15:08:12 +0000136};
137
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000138class CFErrorDerefBug : public BugType {
139public:
140 CFErrorDerefBug() : BugType("CFErrorRef* null dereference",
141 "Coding conventions (Apple)") {}
142};
Ted Kremenekf0673e42008-09-18 21:25:13 +0000143
Ted Kremenekf0673e42008-09-18 21:25:13 +0000144}
145
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000146namespace {
147class NSOrCFErrorDerefChecker
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000148 : public Checker< check::Location,
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000149 check::Event<ImplicitNullDerefEvent> > {
150 mutable IdentifierInfo *NSErrorII, *CFErrorII;
151public:
152 bool ShouldCheckNSError, ShouldCheckCFError;
153 NSOrCFErrorDerefChecker() : NSErrorII(0), CFErrorII(0),
154 ShouldCheckNSError(0), ShouldCheckCFError(0) { }
Mike Stump11289f42009-09-09 15:08:12 +0000155
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000156 void checkLocation(SVal loc, bool isLoad, CheckerContext &C) const;
157 void checkEvent(ImplicitNullDerefEvent event) const;
158};
159}
Ted Kremenekf0673e42008-09-18 21:25:13 +0000160
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000161namespace { struct NSErrorOut {}; }
162namespace { struct CFErrorOut {}; }
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000163
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000164typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag;
165
166namespace clang {
167namespace ento {
168 template <>
Ted Kremenek001fd5b2011-08-15 22:09:50 +0000169 struct ProgramStateTrait<NSErrorOut> : public ProgramStatePartialTrait<ErrorOutFlag> {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000170 static void *GDMIndex() { static int index = 0; return &index; }
171 };
172 template <>
Ted Kremenek001fd5b2011-08-15 22:09:50 +0000173 struct ProgramStateTrait<CFErrorOut> : public ProgramStatePartialTrait<ErrorOutFlag> {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000174 static void *GDMIndex() { static int index = 0; return &index; }
175 };
176}
177}
178
179template <typename T>
Ted Kremenek001fd5b2011-08-15 22:09:50 +0000180static bool hasFlag(SVal val, const ProgramState *state) {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000181 if (SymbolRef sym = val.getAsSymbol())
182 if (const unsigned *attachedFlags = state->get<T>(sym))
183 return *attachedFlags;
184 return false;
185}
186
187template <typename T>
Ted Kremenek001fd5b2011-08-15 22:09:50 +0000188static void setFlag(const ProgramState *state, SVal val, CheckerContext &C) {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000189 // We tag the symbol that the SVal wraps.
190 if (SymbolRef sym = val.getAsSymbol())
191 C.addTransition(state->set<T>(sym, true));
192}
193
Argyrios Kyrtzidisa9a0f5d2011-03-01 01:59:41 +0000194static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) {
195 const StackFrameContext *
196 SFC = C.getPredecessor()->getLocationContext()->getCurrentStackFrame();
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000197 if (const loc::MemRegionVal* X = dyn_cast<loc::MemRegionVal>(&val)) {
198 const MemRegion* R = X->getRegion();
199 if (const VarRegion *VR = R->getAs<VarRegion>())
Argyrios Kyrtzidisa9a0f5d2011-03-01 01:59:41 +0000200 if (const StackArgumentsSpaceRegion *
201 stackReg = dyn_cast<StackArgumentsSpaceRegion>(VR->getMemorySpace()))
202 if (stackReg->getStackFrame() == SFC)
203 return VR->getValueType();
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000204 }
205
206 return QualType();
207}
208
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000209void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
210 CheckerContext &C) const {
211 if (!isLoad)
212 return;
213 if (loc.isUndef() || !isa<Loc>(loc))
Ted Kremenekf0673e42008-09-18 21:25:13 +0000214 return;
Mike Stump11289f42009-09-09 15:08:12 +0000215
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000216 ASTContext &Ctx = C.getASTContext();
Ted Kremenek001fd5b2011-08-15 22:09:50 +0000217 const ProgramState *state = C.getState();
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000218
219 // If we are loading from NSError**/CFErrorRef* parameter, mark the resulting
220 // SVal so that we can later check it when handling the
221 // ImplicitNullDerefEvent event.
222 // FIXME: Cumbersome! Maybe add hook at construction of SVals at start of
223 // function ?
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000224
Argyrios Kyrtzidisa9a0f5d2011-03-01 01:59:41 +0000225 QualType parmT = parameterTypeFromSVal(loc, C);
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000226 if (parmT.isNull())
227 return;
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000228
229 if (!NSErrorII)
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000230 NSErrorII = &Ctx.Idents.get("NSError");
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000231 if (!CFErrorII)
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000232 CFErrorII = &Ctx.Idents.get("CFErrorRef");
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000233
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000234 if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000235 setFlag<NSErrorOut>(state, state->getSVal(cast<Loc>(loc)), C);
236 return;
237 }
238
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000239 if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) {
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000240 setFlag<CFErrorOut>(state, state->getSVal(cast<Loc>(loc)), C);
241 return;
242 }
243}
244
245void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
246 if (event.IsLoad)
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000247 return;
Mike Stump11289f42009-09-09 15:08:12 +0000248
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000249 SVal loc = event.Location;
Ted Kremenek001fd5b2011-08-15 22:09:50 +0000250 const ProgramState *state = event.SinkNode->getState();
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000251 BugReporter &BR = *event.BR;
Mike Stump11289f42009-09-09 15:08:12 +0000252
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000253 bool isNSError = hasFlag<NSErrorOut>(loc, state);
254 bool isCFError = false;
255 if (!isNSError)
256 isCFError = hasFlag<CFErrorOut>(loc, state);
Ted Kremenekf0673e42008-09-18 21:25:13 +0000257
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000258 if (!(isNSError || isCFError))
259 return;
Mike Stump11289f42009-09-09 15:08:12 +0000260
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000261 // Storing to possible null NSError/CFErrorRef out parameter.
262
263 // Emit an error.
264 std::string err;
265 llvm::raw_string_ostream os(err);
266 os << "Potential null dereference. According to coding standards ";
267
268 if (isNSError)
269 os << "in 'Creating and Returning NSError Objects' the parameter '";
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000270 else
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000271 os << "documented in CoreFoundation/CFError.h the parameter '";
Mike Stump11289f42009-09-09 15:08:12 +0000272
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000273 os << "' may be null.";
Mike Stump11289f42009-09-09 15:08:12 +0000274
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000275 BugType *bug = 0;
276 if (isNSError)
277 bug = new NSErrorDerefBug();
278 else
279 bug = new CFErrorDerefBug();
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000280 BugReport *report = new BugReport(*bug, os.str(),
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000281 event.SinkNode);
282 BR.EmitReport(report);
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000283}
284
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000285static bool IsNSError(QualType T, IdentifierInfo *II) {
Ted Kremenekf0673e42008-09-18 21:25:13 +0000286
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000287 const PointerType* PPT = T->getAs<PointerType>();
Steve Naroff7cae42b2009-07-10 23:34:53 +0000288 if (!PPT)
289 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000290
Steve Naroff7cae42b2009-07-10 23:34:53 +0000291 const ObjCObjectPointerType* PT =
John McCall9dd450b2009-09-21 23:43:11 +0000292 PPT->getPointeeType()->getAs<ObjCObjectPointerType>();
Steve Naroff7cae42b2009-07-10 23:34:53 +0000293
294 if (!PT)
295 return false;
Mike Stump11289f42009-09-09 15:08:12 +0000296
Steve Naroff7cae42b2009-07-10 23:34:53 +0000297 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
Mike Stump11289f42009-09-09 15:08:12 +0000298
Steve Naroff7cae42b2009-07-10 23:34:53 +0000299 // FIXME: Can ID ever be NULL?
300 if (ID)
301 return II == ID->getIdentifier();
Mike Stump11289f42009-09-09 15:08:12 +0000302
Steve Naroff7cae42b2009-07-10 23:34:53 +0000303 return false;
Ted Kremenekf0673e42008-09-18 21:25:13 +0000304}
Ted Kremenekb42f4822008-09-18 23:09:54 +0000305
Argyrios Kyrtzidis456b18c2011-03-01 01:47:48 +0000306static bool IsCFError(QualType T, IdentifierInfo *II) {
307 const PointerType* PPT = T->getAs<PointerType>();
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000308 if (!PPT) return false;
Mike Stump11289f42009-09-09 15:08:12 +0000309
John McCall9dd450b2009-09-21 23:43:11 +0000310 const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>();
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000311 if (!TT) return false;
312
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000313 return TT->getDecl()->getIdentifier() == II;
Ted Kremenek3aa89a92008-10-01 23:24:09 +0000314}
315
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000316void ento::registerNSErrorChecker(CheckerManager &mgr) {
317 mgr.registerChecker<NSErrorMethodChecker>();
318 NSOrCFErrorDerefChecker *
319 checker = mgr.registerChecker<NSOrCFErrorDerefChecker>();
320 checker->ShouldCheckNSError = true;
321}
Mike Stump11289f42009-09-09 15:08:12 +0000322
Argyrios Kyrtzidis2c49ec72011-02-28 17:36:18 +0000323void ento::registerCFErrorChecker(CheckerManager &mgr) {
324 mgr.registerChecker<CFErrorFunctionChecker>();
325 NSOrCFErrorDerefChecker *
326 checker = mgr.registerChecker<NSOrCFErrorDerefChecker>();
327 checker->ShouldCheckCFError = true;
Ted Kremenekb42f4822008-09-18 23:09:54 +0000328}