1 //=- NSErrorCheckerer.cpp - Coding conventions for uses of NSError -*- C++ -*-==//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
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.
14 // File under feature request PR 2600.
16 //===----------------------------------------------------------------------===//
18 #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
19 #include "clang/StaticAnalyzer/BugReporter/BugType.h"
20 #include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h"
21 #include "clang/StaticAnalyzer/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"
27 using namespace clang
;
31 class NSErrorChecker
: public BugType
{
33 const bool isNSErrorWarning
;
34 IdentifierInfo
* const II
;
37 void CheckSignature(const ObjCMethodDecl
& MD
, QualType
& ResultTy
,
38 llvm::SmallVectorImpl
<VarDecl
*>& ErrorParams
);
40 void CheckSignature(const FunctionDecl
& MD
, QualType
& ResultTy
,
41 llvm::SmallVectorImpl
<VarDecl
*>& ErrorParams
);
43 bool CheckNSErrorArgument(QualType ArgTy
);
44 bool CheckCFErrorArgument(QualType ArgTy
);
46 void CheckParamDeref(const VarDecl
*V
, const LocationContext
*LC
,
47 const GRState
*state
, BugReporter
& BR
);
49 void EmitRetTyWarning(BugReporter
& BR
, const Decl
& CodeDecl
);
52 NSErrorChecker(const Decl
&D
, bool isNSError
, ExprEngine
& eng
)
53 : BugType(isNSError
? "NSError** null dereference"
54 : "CFErrorRef* null dereference",
55 "Coding conventions (Apple)"),
57 isNSErrorWarning(isNSError
),
58 II(&eng
.getContext().Idents
.get(isNSErrorWarning
? "NSError":"CFErrorRef")),
61 void FlushReports(BugReporter
& BR
);
64 } // end anonymous namespace
66 void ento::RegisterNSErrorChecks(BugReporter
& BR
, ExprEngine
&Eng
,
68 BR
.Register(new NSErrorChecker(D
, true, Eng
));
69 BR
.Register(new NSErrorChecker(D
, false, Eng
));
72 void NSErrorChecker::FlushReports(BugReporter
& BR
) {
73 // Get the analysis engine and the exploded analysis graph.
74 ExplodedGraph
& G
= Eng
.getGraph();
76 // Get the ASTContext, which is useful for querying type information.
77 ASTContext
&Ctx
= BR
.getContext();
80 llvm::SmallVector
<VarDecl
*, 5> ErrorParams
;
82 if (const ObjCMethodDecl
* MD
= dyn_cast
<ObjCMethodDecl
>(&CodeDecl
))
83 CheckSignature(*MD
, ResultTy
, ErrorParams
);
84 else if (const FunctionDecl
* FD
= dyn_cast
<FunctionDecl
>(&CodeDecl
))
85 CheckSignature(*FD
, ResultTy
, ErrorParams
);
89 if (ErrorParams
.empty())
92 if (ResultTy
== Ctx
.VoidTy
) EmitRetTyWarning(BR
, CodeDecl
);
94 for (ExplodedGraph::roots_iterator RI
=G
.roots_begin(), RE
=G
.roots_end();
96 // Scan the parameters for an implicit null dereference.
97 for (llvm::SmallVectorImpl
<VarDecl
*>::iterator I
=ErrorParams
.begin(),
98 E
=ErrorParams
.end(); I
!=E
; ++I
)
99 CheckParamDeref(*I
, (*RI
)->getLocationContext(), (*RI
)->getState(), BR
);
103 void NSErrorChecker::EmitRetTyWarning(BugReporter
& BR
, const Decl
& CodeDecl
) {
105 llvm::raw_string_ostream
os(sbuf
);
107 if (isa
<ObjCMethodDecl
>(CodeDecl
))
113 os
<< (isNSErrorWarning
? "NSError**" : "CFErrorRef*");
114 os
<< " should have a non-void return value to indicate whether or not an "
117 BR
.EmitBasicReport(isNSErrorWarning
118 ? "Bad return type when passing NSError**"
119 : "Bad return type when passing CFError*",
120 getCategory(), os
.str(),
121 CodeDecl
.getLocation());
125 NSErrorChecker::CheckSignature(const ObjCMethodDecl
& M
, QualType
& ResultTy
,
126 llvm::SmallVectorImpl
<VarDecl
*>& ErrorParams
) {
128 ResultTy
= M
.getResultType();
130 for (ObjCMethodDecl::param_iterator I
=M
.param_begin(),
131 E
=M
.param_end(); I
!=E
; ++I
) {
133 QualType T
= (*I
)->getType();
135 if (isNSErrorWarning
) {
136 if (CheckNSErrorArgument(T
)) ErrorParams
.push_back(*I
);
138 else if (CheckCFErrorArgument(T
))
139 ErrorParams
.push_back(*I
);
144 NSErrorChecker::CheckSignature(const FunctionDecl
& F
, QualType
& ResultTy
,
145 llvm::SmallVectorImpl
<VarDecl
*>& ErrorParams
) {
147 ResultTy
= F
.getResultType();
149 for (FunctionDecl::param_const_iterator I
= F
.param_begin(),
150 E
= F
.param_end(); I
!= E
; ++I
) {
152 QualType T
= (*I
)->getType();
154 if (isNSErrorWarning
) {
155 if (CheckNSErrorArgument(T
)) ErrorParams
.push_back(*I
);
157 else if (CheckCFErrorArgument(T
))
158 ErrorParams
.push_back(*I
);
163 bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy
) {
165 const PointerType
* PPT
= ArgTy
->getAs
<PointerType
>();
169 const ObjCObjectPointerType
* PT
=
170 PPT
->getPointeeType()->getAs
<ObjCObjectPointerType
>();
175 const ObjCInterfaceDecl
*ID
= PT
->getInterfaceDecl();
177 // FIXME: Can ID ever be NULL?
179 return II
== ID
->getIdentifier();
184 bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy
) {
186 const PointerType
* PPT
= ArgTy
->getAs
<PointerType
>();
187 if (!PPT
) return false;
189 const TypedefType
* TT
= PPT
->getPointeeType()->getAs
<TypedefType
>();
190 if (!TT
) return false;
192 return TT
->getDecl()->getIdentifier() == II
;
195 void NSErrorChecker::CheckParamDeref(const VarDecl
*Param
,
196 const LocationContext
*LC
,
197 const GRState
*rootState
,
200 SVal ParamL
= rootState
->getLValue(Param
, LC
);
201 const MemRegion
* ParamR
= cast
<loc::MemRegionVal
>(ParamL
).getRegionAs
<VarRegion
>();
202 assert (ParamR
&& "Parameters always have VarRegions.");
203 SVal ParamSVal
= rootState
->getSVal(ParamR
);
205 // FIXME: For now assume that ParamSVal is symbolic. We need to generalize
207 SymbolRef ParamSym
= ParamSVal
.getAsLocSymbol();
211 // Iterate over the implicit-null dereferences.
212 ExplodedNode
*const* I
, *const* E
;
213 llvm::tie(I
, E
) = GetImplicitNullDereferences(Eng
);
214 for ( ; I
!= E
; ++I
) {
215 const GRState
*state
= (*I
)->getState();
216 SVal location
= state
->getSVal((*I
)->getLocationAs
<StmtPoint
>()->getStmt());
217 if (location
.getAsSymbol() != ParamSym
)
222 llvm::raw_string_ostream
os(sbuf
);
223 os
<< "Potential null dereference. According to coding standards ";
225 if (isNSErrorWarning
)
226 os
<< "in 'Creating and Returning NSError Objects' the parameter '";
228 os
<< "documented in CoreFoundation/CFError.h the parameter '";
230 os
<< Param
<< "' may be null.";
232 BugReport
*report
= new BugReport(*this, os
.str(), *I
);
233 // FIXME: Notable symbols are now part of the report. We should
234 // add support for notable symbols in BugReport.
235 // BR.addNotableSymbol(SV->getSymbol());
236 BR
.EmitReport(report
);