]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/Checker/NSErrorChecker.cpp
Update clang to r94309.
[FreeBSD/FreeBSD.git] / lib / Checker / NSErrorChecker.cpp
1 //=- NSErrorCheckerer.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 "clang/Checker/Checkers/LocalCheckers.h"
19 #include "clang/Checker/BugReporter/BugReporter.h"
20 #include "clang/Checker/PathSensitive/GRExprEngine.h"
21 #include "clang/Checker/Checkers/DereferenceChecker.h"
22 #include "BasicObjCFoundationChecks.h"
23 #include "clang/AST/DeclObjC.h"
24 #include "clang/AST/Decl.h"
25 #include "llvm/ADT/SmallVector.h"
26
27 using namespace clang;
28
29 namespace {
30 class NSErrorChecker : public BugType {
31   const Decl &CodeDecl;
32   const bool isNSErrorWarning;
33   IdentifierInfo * const II;
34   GRExprEngine &Eng;
35
36   void CheckSignature(const ObjCMethodDecl& MD, QualType& ResultTy,
37                       llvm::SmallVectorImpl<VarDecl*>& ErrorParams);
38
39   void CheckSignature(const FunctionDecl& MD, QualType& ResultTy,
40                       llvm::SmallVectorImpl<VarDecl*>& ErrorParams);
41
42   bool CheckNSErrorArgument(QualType ArgTy);
43   bool CheckCFErrorArgument(QualType ArgTy);
44
45   void CheckParamDeref(const VarDecl *V, const LocationContext *LC,
46                        const GRState *state, BugReporter& BR);
47
48   void EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl);
49
50 public:
51   NSErrorChecker(const Decl &D, bool isNSError, GRExprEngine& eng)
52     : BugType(isNSError ? "NSError** null dereference"
53                         : "CFErrorRef* null dereference",
54               "Coding conventions (Apple)"),
55     CodeDecl(D),
56     isNSErrorWarning(isNSError),
57     II(&eng.getContext().Idents.get(isNSErrorWarning ? "NSError":"CFErrorRef")),
58     Eng(eng) {}
59
60   void FlushReports(BugReporter& BR);
61 };
62
63 } // end anonymous namespace
64
65 void clang::RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng,
66                                   const Decl &D) {
67   BR.Register(new NSErrorChecker(D, true, Eng));
68   BR.Register(new NSErrorChecker(D, false, Eng));
69 }
70
71 void NSErrorChecker::FlushReports(BugReporter& BR) {
72   // Get the analysis engine and the exploded analysis graph.
73   ExplodedGraph& G = Eng.getGraph();
74
75   // Get the ASTContext, which is useful for querying type information.
76   ASTContext &Ctx = BR.getContext();
77
78   QualType ResultTy;
79   llvm::SmallVector<VarDecl*, 5> ErrorParams;
80
81   if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CodeDecl))
82     CheckSignature(*MD, ResultTy, ErrorParams);
83   else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(&CodeDecl))
84     CheckSignature(*FD, ResultTy, ErrorParams);
85   else
86     return;
87
88   if (ErrorParams.empty())
89     return;
90
91   if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl);
92
93   for (ExplodedGraph::roots_iterator RI=G.roots_begin(), RE=G.roots_end();
94        RI!=RE; ++RI) {
95     // Scan the parameters for an implicit null dereference.
96     for (llvm::SmallVectorImpl<VarDecl*>::iterator I=ErrorParams.begin(),
97           E=ErrorParams.end(); I!=E; ++I)
98         CheckParamDeref(*I, (*RI)->getLocationContext(), (*RI)->getState(), BR);
99   }
100 }
101
102 void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) {
103   std::string sbuf;
104   llvm::raw_string_ostream os(sbuf);
105
106   if (isa<ObjCMethodDecl>(CodeDecl))
107     os << "Method";
108   else
109     os << "Function";
110
111   os << " accepting ";
112   os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*");
113   os << " should have a non-void return value to indicate whether or not an "
114         "error occurred";
115
116   BR.EmitBasicReport(isNSErrorWarning
117                      ? "Bad return type when passing NSError**"
118                      : "Bad return type when passing CFError*",
119                      getCategory(), os.str(),
120                      CodeDecl.getLocation());
121 }
122
123 void
124 NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy,
125                              llvm::SmallVectorImpl<VarDecl*>& ErrorParams) {
126
127   ResultTy = M.getResultType();
128
129   for (ObjCMethodDecl::param_iterator I=M.param_begin(),
130        E=M.param_end(); I!=E; ++I)  {
131
132     QualType T = (*I)->getType();
133
134     if (isNSErrorWarning) {
135       if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I);
136     }
137     else if (CheckCFErrorArgument(T))
138       ErrorParams.push_back(*I);
139   }
140 }
141
142 void
143 NSErrorChecker::CheckSignature(const FunctionDecl& F, QualType& ResultTy,
144                              llvm::SmallVectorImpl<VarDecl*>& ErrorParams) {
145
146   ResultTy = F.getResultType();
147
148   for (FunctionDecl::param_const_iterator I = F.param_begin(),
149                                           E = F.param_end(); I != E; ++I)  {
150
151     QualType T = (*I)->getType();
152
153     if (isNSErrorWarning) {
154       if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I);
155     }
156     else if (CheckCFErrorArgument(T))
157       ErrorParams.push_back(*I);
158   }
159 }
160
161
162 bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) {
163
164   const PointerType* PPT = ArgTy->getAs<PointerType>();
165   if (!PPT)
166     return false;
167
168   const ObjCObjectPointerType* PT =
169     PPT->getPointeeType()->getAs<ObjCObjectPointerType>();
170
171   if (!PT)
172     return false;
173
174   const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
175
176   // FIXME: Can ID ever be NULL?
177   if (ID)
178     return II == ID->getIdentifier();
179
180   return false;
181 }
182
183 bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) {
184
185   const PointerType* PPT = ArgTy->getAs<PointerType>();
186   if (!PPT) return false;
187
188   const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>();
189   if (!TT) return false;
190
191   return TT->getDecl()->getIdentifier() == II;
192 }
193
194 void NSErrorChecker::CheckParamDeref(const VarDecl *Param,
195                                    const LocationContext *LC,
196                                    const GRState *rootState,
197                                    BugReporter& BR) {
198
199   SVal ParamL = rootState->getLValue(Param, LC);
200   const MemRegion* ParamR = cast<loc::MemRegionVal>(ParamL).getRegionAs<VarRegion>();
201   assert (ParamR && "Parameters always have VarRegions.");
202   SVal ParamSVal = rootState->getSVal(ParamR);
203
204   // FIXME: For now assume that ParamSVal is symbolic.  We need to generalize
205   // this later.
206   SymbolRef ParamSym = ParamSVal.getAsLocSymbol();
207   if (!ParamSym)
208     return;
209
210   // Iterate over the implicit-null dereferences.
211   ExplodedNode *const* I,  *const* E;
212   llvm::tie(I, E) = GetImplicitNullDereferences(Eng);
213   for ( ; I != E; ++I) {
214     const GRState *state = (*I)->getState();
215     SVal location = state->getSVal((*I)->getLocationAs<StmtPoint>()->getStmt());
216     if (location.getAsSymbol() != ParamSym)
217       continue;
218
219     // Emit an error.
220     std::string sbuf;
221     llvm::raw_string_ostream os(sbuf);
222       os << "Potential null dereference.  According to coding standards ";
223
224     if (isNSErrorWarning)
225       os << "in 'Creating and Returning NSError Objects' the parameter '";
226     else
227       os << "documented in CoreFoundation/CFError.h the parameter '";
228
229     os << Param->getNameAsString() << "' may be null.";
230
231     BugReport *report = new BugReport(*this, os.str(), *I);
232     // FIXME: Notable symbols are now part of the report.  We should
233     //  add support for notable symbols in BugReport.
234     //    BR.addNotableSymbol(SV->getSymbol());
235     BR.EmitReport(report);
236   }
237 }