]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
Copy head to stable/9 as part of 9.0-RELEASE release cycle.
[FreeBSD/stable/9.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / NSErrorChecker.cpp
1 //=- NSErrorChecker.cpp - Coding conventions for uses of NSError -*- C++ -*-==//
2 //
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
18 #include "ClangSACheckers.h"
19 #include "clang/StaticAnalyzer/Core/Checker.h"
20 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h"
23 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24 #include "clang/AST/DeclObjC.h"
25 #include "clang/AST/Decl.h"
26 #include "llvm/ADT/SmallVector.h"
27
28 using namespace clang;
29 using namespace ento;
30
31 static bool IsNSError(QualType T, IdentifierInfo *II);
32 static bool IsCFError(QualType T, IdentifierInfo *II);
33
34 //===----------------------------------------------------------------------===//
35 // NSErrorMethodChecker
36 //===----------------------------------------------------------------------===//
37
38 namespace {
39 class NSErrorMethodChecker
40     : public Checker< check::ASTDecl<ObjCMethodDecl> > {
41   mutable IdentifierInfo *II;
42
43 public:
44   NSErrorMethodChecker() : II(0) { }
45
46   void checkASTDecl(const ObjCMethodDecl *D,
47                     AnalysisManager &mgr, BugReporter &BR) const;
48 };
49 }
50
51 void 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)  {
65     if (IsNSError((*I)->getType(), II)) {
66       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
84 namespace {
85 class CFErrorFunctionChecker
86     : public Checker< check::ASTDecl<FunctionDecl> > {
87   mutable IdentifierInfo *II;
88
89 public:
90   CFErrorFunctionChecker() : II(0) { }
91
92   void checkASTDecl(const FunctionDecl *D,
93                     AnalysisManager &mgr, BugReporter &BR) const;
94 };
95 }
96
97 void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
98                                         AnalysisManager &mgr,
99                                         BugReporter &BR) const {
100   if (!D->doesThisDeclarationHaveABody())
101     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)  {
111     if (IsCFError((*I)->getType(), II)) {
112       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
130 namespace {
131
132 class NSErrorDerefBug : public BugType {
133 public:
134   NSErrorDerefBug() : BugType("NSError** null dereference",
135                               "Coding conventions (Apple)") {}
136 };
137
138 class CFErrorDerefBug : public BugType {
139 public:
140   CFErrorDerefBug() : BugType("CFErrorRef* null dereference",
141                               "Coding conventions (Apple)") {}
142 };
143
144 }
145
146 namespace {
147 class NSOrCFErrorDerefChecker
148     : public Checker< check::Location,
149                         check::Event<ImplicitNullDerefEvent> > {
150   mutable IdentifierInfo *NSErrorII, *CFErrorII;
151 public:
152   bool ShouldCheckNSError, ShouldCheckCFError;
153   NSOrCFErrorDerefChecker() : NSErrorII(0), CFErrorII(0),
154                               ShouldCheckNSError(0), ShouldCheckCFError(0) { }
155
156   void checkLocation(SVal loc, bool isLoad, CheckerContext &C) const;
157   void checkEvent(ImplicitNullDerefEvent event) const;
158 };
159 }
160
161 namespace { struct NSErrorOut {}; }
162 namespace { struct CFErrorOut {}; }
163
164 typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag;
165
166 namespace clang {
167 namespace ento {
168   template <>
169   struct GRStateTrait<NSErrorOut> : public GRStatePartialTrait<ErrorOutFlag> {  
170     static void *GDMIndex() { static int index = 0; return &index; }
171   };
172   template <>
173   struct GRStateTrait<CFErrorOut> : public GRStatePartialTrait<ErrorOutFlag> {  
174     static void *GDMIndex() { static int index = 0; return &index; }
175   };
176 }
177 }
178
179 template <typename T>
180 static bool hasFlag(SVal val, const GRState *state) {
181   if (SymbolRef sym = val.getAsSymbol())
182     if (const unsigned *attachedFlags = state->get<T>(sym))
183       return *attachedFlags;
184   return false;
185 }
186
187 template <typename T>
188 static void setFlag(const GRState *state, SVal val, CheckerContext &C) {
189   // We tag the symbol that the SVal wraps.
190   if (SymbolRef sym = val.getAsSymbol())
191     C.addTransition(state->set<T>(sym, true));
192 }
193
194 static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) {
195   const StackFrameContext *
196     SFC = C.getPredecessor()->getLocationContext()->getCurrentStackFrame();
197   if (const loc::MemRegionVal* X = dyn_cast<loc::MemRegionVal>(&val)) {
198     const MemRegion* R = X->getRegion();
199     if (const VarRegion *VR = R->getAs<VarRegion>())
200       if (const StackArgumentsSpaceRegion *
201           stackReg = dyn_cast<StackArgumentsSpaceRegion>(VR->getMemorySpace()))
202         if (stackReg->getStackFrame() == SFC)
203           return VR->getValueType();
204   }
205
206   return QualType();
207 }
208
209 void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
210                                             CheckerContext &C) const {
211   if (!isLoad)
212     return;
213   if (loc.isUndef() || !isa<Loc>(loc))
214     return;
215
216   ASTContext &Ctx = C.getASTContext();
217   const GRState *state = C.getState();
218
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 ?
224
225   QualType parmT = parameterTypeFromSVal(loc, C);
226   if (parmT.isNull())
227     return;
228
229   if (!NSErrorII)
230     NSErrorII = &Ctx.Idents.get("NSError");
231   if (!CFErrorII)
232     CFErrorII = &Ctx.Idents.get("CFErrorRef");
233
234   if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) {
235     setFlag<NSErrorOut>(state, state->getSVal(cast<Loc>(loc)), C);
236     return;
237   }
238
239   if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) {
240     setFlag<CFErrorOut>(state, state->getSVal(cast<Loc>(loc)), C);
241     return;
242   }
243 }
244
245 void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
246   if (event.IsLoad)
247     return;
248
249   SVal loc = event.Location;
250   const GRState *state = event.SinkNode->getState();
251   BugReporter &BR = *event.BR;
252
253   bool isNSError = hasFlag<NSErrorOut>(loc, state);
254   bool isCFError = false;
255   if (!isNSError)
256     isCFError = hasFlag<CFErrorOut>(loc, state);
257
258   if (!(isNSError || isCFError))
259     return;
260
261   // 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 '";
270   else
271     os << "documented in CoreFoundation/CFError.h the parameter '";
272
273   os  << "' may be null.";
274
275   BugType *bug = 0;
276   if (isNSError)
277     bug = new NSErrorDerefBug();
278   else
279     bug = new CFErrorDerefBug();
280   EnhancedBugReport *report = new EnhancedBugReport(*bug, os.str(),
281                                                     event.SinkNode);
282   BR.EmitReport(report);
283 }
284
285 static bool IsNSError(QualType T, IdentifierInfo *II) {
286
287   const PointerType* PPT = T->getAs<PointerType>();
288   if (!PPT)
289     return false;
290
291   const ObjCObjectPointerType* PT =
292     PPT->getPointeeType()->getAs<ObjCObjectPointerType>();
293
294   if (!PT)
295     return false;
296
297   const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
298
299   // FIXME: Can ID ever be NULL?
300   if (ID)
301     return II == ID->getIdentifier();
302
303   return false;
304 }
305
306 static bool IsCFError(QualType T, IdentifierInfo *II) {
307   const PointerType* PPT = T->getAs<PointerType>();
308   if (!PPT) return false;
309
310   const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>();
311   if (!TT) return false;
312
313   return TT->getDecl()->getIdentifier() == II;
314 }
315
316 void ento::registerNSErrorChecker(CheckerManager &mgr) {
317   mgr.registerChecker<NSErrorMethodChecker>();
318   NSOrCFErrorDerefChecker *
319     checker = mgr.registerChecker<NSOrCFErrorDerefChecker>();
320   checker->ShouldCheckNSError = true;
321 }
322
323 void ento::registerCFErrorChecker(CheckerManager &mgr) {
324   mgr.registerChecker<CFErrorFunctionChecker>();
325   NSOrCFErrorDerefChecker *
326     checker = mgr.registerChecker<NSOrCFErrorDerefChecker>();
327   checker->ShouldCheckCFError = true;
328 }