blob: 919c17d0646ab7cc82b8745e03f940ee1b14d1b7 [file] [log] [blame]
Ted Kremenekf45d18c2008-09-18 06:33:41 +00001//=- CheckNSError.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/Analysis/LocalCheckers.h"
19#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000020#include "clang/Analysis/PathSensitive/GRExprEngine.h"
21#include "BasicObjCFoundationChecks.h"
22#include "llvm/Support/Compiler.h"
Ted Kremenekf45d18c2008-09-18 06:33:41 +000023#include "clang/AST/DeclObjC.h"
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000024#include "clang/AST/Decl.h"
25#include "llvm/ADT/SmallVector.h"
Ted Kremenekf45d18c2008-09-18 06:33:41 +000026
27using namespace clang;
28
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000029namespace {
30class VISIBILITY_HIDDEN NSErrorCheck : public BugTypeCacheLocation {
31
32 void EmitGRWarnings(GRBugReporter& BR);
Ted Kremenekf45d18c2008-09-18 06:33:41 +000033
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000034 void CheckSignature(ObjCMethodDecl& MD, QualType& ResultTy,
Ted Kremenekcc9ac412008-10-01 23:24:09 +000035 llvm::SmallVectorImpl<VarDecl*>& NSErrorParams,
36 llvm::SmallVectorImpl<VarDecl*>& CFErrorParams,
37 IdentifierInfo* NSErrorII,
38 IdentifierInfo* CFErrorII);
39
40 void CheckSignature(FunctionDecl& MD, QualType& ResultTy,
41 llvm::SmallVectorImpl<VarDecl*>& NSErrorParams,
42 llvm::SmallVectorImpl<VarDecl*>& CFErrorParams,
43 IdentifierInfo* NSErrorII,
44 IdentifierInfo* CFErrorII);
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000045
Ted Kremenekcc9ac412008-10-01 23:24:09 +000046 bool CheckNSErrorArgument(QualType ArgTy, IdentifierInfo* NSErrorII);
47 bool CheckCFErrorArgument(QualType ArgTy, IdentifierInfo* CFErrorII);
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000048
Ted Kremenek7360fda2008-09-18 23:09:54 +000049 void CheckParamDeref(VarDecl* V, GRStateRef state, GRExprEngine& Eng,
Ted Kremenekcc9ac412008-10-01 23:24:09 +000050 GRBugReporter& BR, bool isNErrorWarning);
51
52 void EmitRetTyWarning(BugReporter& BR, Decl& CodeDecl, bool isNSErrorWarning);
Ted Kremenek7360fda2008-09-18 23:09:54 +000053
54 const char* desc;
Ted Kremenekcc9ac412008-10-01 23:24:09 +000055 const char* name;
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000056public:
Ted Kremenek7360fda2008-09-18 23:09:54 +000057 NSErrorCheck() : desc(0) {}
58
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000059 void EmitWarnings(BugReporter& BR) { EmitGRWarnings(cast<GRBugReporter>(BR));}
Ted Kremenekcc9ac412008-10-01 23:24:09 +000060 const char* getName() const { return name; }
Ted Kremenek7360fda2008-09-18 23:09:54 +000061 const char* getDescription() const { return desc; }
Ted Kremenek62059e82008-09-21 06:57:40 +000062 const char* getCategory() const { return "Coding Conventions (Apple)"; }
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000063};
64
65} // end anonymous namespace
66
67BugType* clang::CreateNSErrorCheck() {
68 return new NSErrorCheck();
69}
70
71void NSErrorCheck::EmitGRWarnings(GRBugReporter& BR) {
72 // Get the analysis engine and the exploded analysis graph.
73 GRExprEngine& Eng = BR.getEngine();
74 GRExprEngine::GraphTy& G = Eng.getGraph();
75
76 // Get the declaration of the method/function that was analyzed.
77 Decl& CodeDecl = G.getCodeDecl();
Ted Kremenekcc9ac412008-10-01 23:24:09 +000078
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000079 // Get the ASTContext, which is useful for querying type information.
Ted Kremenekf45d18c2008-09-18 06:33:41 +000080 ASTContext &Ctx = BR.getContext();
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000081
82 QualType ResultTy;
Ted Kremenekcc9ac412008-10-01 23:24:09 +000083 llvm::SmallVector<VarDecl*, 5> NSErrorParams;
84 llvm::SmallVector<VarDecl*, 5> CFErrorParams;
85
86 if (ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CodeDecl))
87 CheckSignature(*MD, ResultTy, NSErrorParams, CFErrorParams,
88 &Ctx.Idents.get("NSError"), &Ctx.Idents.get("CFErrorRef"));
89 else if (FunctionDecl* FD = dyn_cast<FunctionDecl>(&CodeDecl))
90 CheckSignature(*FD, ResultTy, NSErrorParams, CFErrorParams,
91 &Ctx.Idents.get("NSError"), &Ctx.Idents.get("CFErrorRef"));
92 else
Ted Kremenekcfdf9b42008-09-18 21:25:13 +000093 return;
94
Ted Kremenekcc9ac412008-10-01 23:24:09 +000095 if (NSErrorParams.empty() && CFErrorParams.empty())
96 return;
97
98 if (ResultTy == Ctx.VoidTy) {
99 if (!NSErrorParams.empty())
100 EmitRetTyWarning(BR, CodeDecl, true);
101 if (!CFErrorParams.empty())
102 EmitRetTyWarning(BR, CodeDecl, false);
Ted Kremenekf45d18c2008-09-18 06:33:41 +0000103 }
Ted Kremenek7360fda2008-09-18 23:09:54 +0000104
Ted Kremenekcc9ac412008-10-01 23:24:09 +0000105 for (GRExprEngine::GraphTy::roots_iterator RI=G.roots_begin(),
106 RE=G.roots_end(); RI!=RE; ++RI) {
107
108 // Scan the NSError** parameters for an implicit null dereference.
109 for (llvm::SmallVectorImpl<VarDecl*>::iterator I=NSErrorParams.begin(),
110 E=NSErrorParams.end(); I!=E; ++I)
111 CheckParamDeref(*I, GRStateRef((*RI)->getState(), Eng.getStateManager()),
112 Eng, BR, true);
113
114 // Scan the CFErrorRef* parameters for an implicit null dereference.
115 for (llvm::SmallVectorImpl<VarDecl*>::iterator I=CFErrorParams.begin(),
116 E=CFErrorParams.end(); I!=E; ++I)
Ted Kremenek7360fda2008-09-18 23:09:54 +0000117 CheckParamDeref(*I, GRStateRef((*RI)->getState(), Eng.getStateManager()),
Ted Kremenekcc9ac412008-10-01 23:24:09 +0000118 Eng, BR, false);
119 }
Ted Kremenekf45d18c2008-09-18 06:33:41 +0000120}
Ted Kremenekcfdf9b42008-09-18 21:25:13 +0000121
Ted Kremenekcc9ac412008-10-01 23:24:09 +0000122void NSErrorCheck::EmitRetTyWarning(BugReporter& BR, Decl& CodeDecl,
123 bool isNSErrorWarning) {
124
125 std::string msg;
126 llvm::raw_string_ostream os(msg);
127
128 if (isa<ObjCMethodDecl>(CodeDecl))
129 os << "Method";
130 else
131 os << "Function";
132
133 os << " accepting ";
134 os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*");
135 os << " should have a non-void return value to indicate whether or not an "
136 "error occured.";
137
138 BR.EmitBasicReport(isNSErrorWarning
139 ? "Bad return type when passing NSError**"
140 : "Bad return type when passing CFError*",
141 getCategory(), os.str().c_str(), CodeDecl.getLocation());
142}
143
144void
145NSErrorCheck::CheckSignature(ObjCMethodDecl& M, QualType& ResultTy,
146 llvm::SmallVectorImpl<VarDecl*>& NSErrorParams,
147 llvm::SmallVectorImpl<VarDecl*>& CFErrorParams,
148 IdentifierInfo* NSErrorII,
149 IdentifierInfo* CFErrorII) {
Ted Kremenekcfdf9b42008-09-18 21:25:13 +0000150
151 ResultTy = M.getResultType();
152
153 for (ObjCMethodDecl::param_iterator I=M.param_begin(),
Ted Kremenekcc9ac412008-10-01 23:24:09 +0000154 E=M.param_end(); I!=E; ++I) {
155
156 QualType T = (*I)->getType();
157
158 if (CheckNSErrorArgument(T, NSErrorII))
159 NSErrorParams.push_back(*I);
160 else if (CheckCFErrorArgument(T, CFErrorII))
161 CFErrorParams.push_back(*I);
162 }
Ted Kremenekcfdf9b42008-09-18 21:25:13 +0000163}
164
Ted Kremenekcc9ac412008-10-01 23:24:09 +0000165void
166NSErrorCheck::CheckSignature(FunctionDecl& F, QualType& ResultTy,
167 llvm::SmallVectorImpl<VarDecl*>& NSErrorParams,
168 llvm::SmallVectorImpl<VarDecl*>& CFErrorParams,
169 IdentifierInfo* NSErrorII,
170 IdentifierInfo* CFErrorII) {
171
172 ResultTy = F.getResultType();
173
174 for (FunctionDecl::param_iterator I=F.param_begin(),
175 E=F.param_end(); I!=E; ++I) {
176
177 QualType T = (*I)->getType();
178
179 if (CheckNSErrorArgument(T, NSErrorII))
180 NSErrorParams.push_back(*I);
181 else if (CheckCFErrorArgument(T, CFErrorII))
182 CFErrorParams.push_back(*I);
183 }
184}
185
186
187bool NSErrorCheck::CheckNSErrorArgument(QualType ArgTy,
188 IdentifierInfo* NSErrorII) {
189
Ted Kremenekcfdf9b42008-09-18 21:25:13 +0000190 const PointerType* PPT = ArgTy->getAsPointerType();
191 if (!PPT) return false;
192
193 const PointerType* PT = PPT->getPointeeType()->getAsPointerType();
194 if (!PT) return false;
195
196 const ObjCInterfaceType *IT =
197 PT->getPointeeType()->getAsObjCInterfaceType();
198
199 if (!IT) return false;
200 return IT->getDecl()->getIdentifier() == NSErrorII;
201}
Ted Kremenek7360fda2008-09-18 23:09:54 +0000202
Ted Kremenekcc9ac412008-10-01 23:24:09 +0000203bool NSErrorCheck::CheckCFErrorArgument(QualType ArgTy,
204 IdentifierInfo* CFErrorII) {
205
206 const PointerType* PPT = ArgTy->getAsPointerType();
207 if (!PPT) return false;
208
209 const TypedefType* TT = PPT->getPointeeType()->getAsTypedefType();
210 if (!TT) return false;
211
212 return TT->getDecl()->getIdentifier() == CFErrorII;
213}
214
Ted Kremenek7360fda2008-09-18 23:09:54 +0000215void NSErrorCheck::CheckParamDeref(VarDecl* Param, GRStateRef rootState,
Ted Kremenekcc9ac412008-10-01 23:24:09 +0000216 GRExprEngine& Eng, GRBugReporter& BR,
217 bool isNSErrorWarning) {
Ted Kremenek7360fda2008-09-18 23:09:54 +0000218
Ted Kremenek993f1c72008-10-17 20:28:54 +0000219 SVal ParamL = rootState.GetLValue(Param);
220 const MemRegion* ParamR = cast<loc::MemRegionVal>(ParamL).getRegionAs<VarRegion>();
221 assert (ParamR && "Parameters always have VarRegions.");
222 SVal ParamSVal = rootState.GetSVal(ParamR);
223
Ted Kremenek7360fda2008-09-18 23:09:54 +0000224
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000225 // FIXME: For now assume that ParamSVal is symbolic. We need to generalize
Ted Kremenek7360fda2008-09-18 23:09:54 +0000226 // this later.
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000227 loc::SymbolVal* SV = dyn_cast<loc::SymbolVal>(&ParamSVal);
Ted Kremenek7360fda2008-09-18 23:09:54 +0000228 if (!SV) return;
229
230 // Iterate over the implicit-null dereferences.
231 for (GRExprEngine::null_deref_iterator I=Eng.implicit_null_derefs_begin(),
232 E=Eng.implicit_null_derefs_end(); I!=E; ++I) {
233
234 GRStateRef state = GRStateRef((*I)->getState(), Eng.getStateManager());
Zhongxing Xu1c96b242008-10-17 05:57:07 +0000235 const SVal* X = state.get<GRState::NullDerefTag>();
236 const loc::SymbolVal* SVX = dyn_cast_or_null<loc::SymbolVal>(X);
Ted Kremenek7360fda2008-09-18 23:09:54 +0000237 if (!SVX || SVX->getSymbol() != SV->getSymbol()) continue;
238
239 // Emit an error.
240 BugReport R(*this, *I);
Ted Kremenekcc9ac412008-10-01 23:24:09 +0000241
242 name = isNSErrorWarning ? "NSError** null dereference"
243 : "CFErrorRef* null dereference";
Ted Kremenek7360fda2008-09-18 23:09:54 +0000244
245 std::string msg;
246 llvm::raw_string_ostream os(msg);
Ted Kremenekcc9ac412008-10-01 23:24:09 +0000247 os << "Potential null dereference. According to coding standards ";
248
249 if (isNSErrorWarning)
250 os << "in 'Creating and Returning NSError Objects' the parameter '";
251 else
252 os << "documented in CoreFoundation/CFError.h the parameter '";
253
Chris Lattnerd9d22dd2008-11-24 05:29:24 +0000254 os << Param->getNameAsString() << "' may be null.";
Ted Kremenek7360fda2008-09-18 23:09:54 +0000255 desc = os.str().c_str();
256
Ted Kremenek37fc8262008-09-18 23:23:19 +0000257 BR.addNotableSymbol(SV->getSymbol());
Ted Kremenekcc9ac412008-10-01 23:24:09 +0000258 BR.EmitWarning(R);
Ted Kremenek7360fda2008-09-18 23:09:54 +0000259 }
260}