 |
|
 |
|
| Files: |
1 |
|
Branches Taken: |
82.9% |
58 / 70 |
| Generated: |
2010-02-10 01:31 |
|
Branches Executed: |
97.1% |
68 / 70 |
| |
|
Line Coverage: |
95.8% |
92 / 96 |
| |
 |
|
 |
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 {
4276: branch 1 taken
0: branch 2 not taken
0: branch 5 not taken
0: branch 6 not taken
30 4276: 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 4276: 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")),
2138: branch 1 taken
2138: branch 2 taken
2138: branch 5 taken
2138: branch 6 taken
58 4276: Eng(eng) {}
59 :
60 : void FlushReports(BugReporter& BR);
61 : };
62 :
63 : } // end anonymous namespace
64 :
65 : void clang::RegisterNSErrorChecks(BugReporter& BR, GRExprEngine &Eng,
66 2138: const Decl &D) {
67 2138: BR.Register(new NSErrorChecker(D, true, Eng));
68 2138: BR.Register(new NSErrorChecker(D, false, Eng));
69 2138: }
70 :
71 4276: void NSErrorChecker::FlushReports(BugReporter& BR) {
72 : // Get the analysis engine and the exploded analysis graph.
73 4276: ExplodedGraph& G = Eng.getGraph();
74 :
75 : // Get the ASTContext, which is useful for querying type information.
76 4276: ASTContext &Ctx = BR.getContext();
77 :
78 4276: QualType ResultTy;
79 4276: llvm::SmallVector<VarDecl*, 5> ErrorParams;
80 :
534: branch 1 taken
3742: branch 2 taken
81 4276: if (const ObjCMethodDecl* MD = dyn_cast<ObjCMethodDecl>(&CodeDecl))
82 534: CheckSignature(*MD, ResultTy, ErrorParams);
48: branch 1 taken
3694: branch 2 taken
83 3742: else if (const FunctionDecl* FD = dyn_cast<FunctionDecl>(&CodeDecl))
84 3694: CheckSignature(*FD, ResultTy, ErrorParams);
85 : else
86 4252: return;
87 :
4204: branch 1 taken
24: branch 2 taken
88 4228: if (ErrorParams.empty())
89 : return;
90 :
8: branch 2 taken
16: branch 3 taken
91 24: if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl);
92 :
24: branch 2 taken
24: branch 3 taken
93 48: 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.
24: branch 1 taken
24: branch 2 taken
96 72: for (llvm::SmallVectorImpl<VarDecl*>::iterator I=ErrorParams.begin(),
97 24: E=ErrorParams.end(); I!=E; ++I)
98 24: CheckParamDeref(*I, (*RI)->getLocationContext(), (*RI)->getState(), BR);
24: branch 1 taken
4252: branch 2 taken
99 4276: }
100 : }
101 :
102 8: void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) {
103 8: std::string sbuf;
104 8: llvm::raw_string_ostream os(sbuf);
105 :
4: branch 1 taken
4: branch 2 taken
106 8: if (isa<ObjCMethodDecl>(CodeDecl))
107 4: os << "Method";
108 : else
109 4: os << "Function";
110 :
111 8: os << " accepting ";
4: branch 0 taken
4: branch 1 taken
112 8: os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*");
113 : os << " should have a non-void return value to indicate whether or not an "
114 8: "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(),
4: branch 4 taken
4: branch 5 taken
120 8: CodeDecl.getLocation());
121 8: }
122 :
123 : void
124 : NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy,
125 534: llvm::SmallVectorImpl<VarDecl*>& ErrorParams) {
126 :
127 534: ResultTy = M.getResultType();
128 :
184: branch 1 taken
534: branch 2 taken
129 1252: for (ObjCMethodDecl::param_iterator I=M.param_begin(),
130 534: E=M.param_end(); I!=E; ++I) {
131 :
132 184: QualType T = (*I)->getType();
133 :
92: branch 0 taken
92: branch 1 taken
134 184: if (isNSErrorWarning) {
8: branch 1 taken
84: branch 2 taken
135 92: if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I);
136 : }
0: branch 1 not taken
92: branch 2 taken
137 92: else if (CheckCFErrorArgument(T))
138 0: ErrorParams.push_back(*I);
139 : }
140 534: }
141 :
142 : void
143 : NSErrorChecker::CheckSignature(const FunctionDecl& F, QualType& ResultTy,
144 3694: llvm::SmallVectorImpl<VarDecl*>& ErrorParams) {
145 :
146 3694: ResultTy = F.getResultType();
147 :
2624: branch 1 taken
3694: branch 2 taken
148 10012: for (FunctionDecl::param_const_iterator I = F.param_begin(),
149 3694: E = F.param_end(); I != E; ++I) {
150 :
151 2624: QualType T = (*I)->getType();
152 :
1312: branch 0 taken
1312: branch 1 taken
153 2624: if (isNSErrorWarning) {
0: branch 1 not taken
1312: branch 2 taken
154 1312: if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I);
155 : }
16: branch 1 taken
1296: branch 2 taken
156 1312: else if (CheckCFErrorArgument(T))
157 16: ErrorParams.push_back(*I);
158 : }
159 3694: }
160 :
161 :
162 1404: bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) {
163 :
164 1404: const PointerType* PPT = ArgTy->getAs<PointerType>();
824: branch 0 taken
580: branch 1 taken
165 1404: if (!PPT)
166 824: return false;
167 :
168 : const ObjCObjectPointerType* PT =
169 580: PPT->getPointeeType()->getAs<ObjCObjectPointerType>();
170 :
556: branch 0 taken
24: branch 1 taken
171 580: if (!PT)
172 556: return false;
173 :
174 24: const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
175 :
176 : // FIXME: Can ID ever be NULL?
24: branch 0 taken
0: branch 1 not taken
177 24: if (ID)
178 24: return II == ID->getIdentifier();
179 :
180 0: return false;
181 : }
182 :
183 1404: bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) {
184 :
185 1404: const PointerType* PPT = ArgTy->getAs<PointerType>();
824: branch 0 taken
580: branch 1 taken
186 1404: if (!PPT) return false;
187 :
188 580: const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>();
504: branch 0 taken
76: branch 1 taken
189 580: if (!TT) return false;
190 :
191 76: return TT->getDecl()->getIdentifier() == II;
192 : }
193 :
194 : void NSErrorChecker::CheckParamDeref(const VarDecl *Param,
195 : const LocationContext *LC,
196 : const GRState *rootState,
197 24: BugReporter& BR) {
198 :
199 24: SVal ParamL = rootState->getLValue(Param, LC);
200 24: const MemRegion* ParamR = cast<loc::MemRegionVal>(ParamL).getRegionAs<VarRegion>();
0: branch 0 not taken
24: branch 1 taken
201 24: assert (ParamR && "Parameters always have VarRegions.");
202 24: SVal ParamSVal = rootState->getSVal(ParamR);
203 :
204 : // FIXME: For now assume that ParamSVal is symbolic. We need to generalize
205 : // this later.
206 24: SymbolRef ParamSym = ParamSVal.getAsLocSymbol();
0: branch 0 not taken
24: branch 1 taken
207 24: if (!ParamSym)
208 0: return;
209 :
210 : // Iterate over the implicit-null dereferences.
211 : ExplodedNode *const* I, *const* E;
212 24: llvm::tie(I, E) = GetImplicitNullDereferences(Eng);
8: branch 3 taken
0: branch 4 not taken
8: branch 5 taken
24: branch 6 taken
213 40: for ( ; I != E; ++I) {
214 8: const GRState *state = (*I)->getState();
215 8: SVal location = state->getSVal((*I)->getLocationAs<StmtPoint>()->getStmt());
0: branch 1 not taken
8: branch 2 taken
216 8: if (location.getAsSymbol() != ParamSym)
217 0: continue;
218 :
219 : // Emit an error.
220 8: std::string sbuf;
221 8: llvm::raw_string_ostream os(sbuf);
222 8: os << "Potential null dereference. According to coding standards ";
223 :
4: branch 0 taken
4: branch 1 taken
224 8: if (isNSErrorWarning)
225 4: os << "in 'Creating and Returning NSError Objects' the parameter '";
226 : else
227 4: os << "documented in CoreFoundation/CFError.h the parameter '";
228 :
229 8: os << Param->getNameAsString() << "' may be null.";
230 :
231 8: 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 8: BR.EmitReport(report);
24: branch 1 taken
0: branch 2 not taken
24: branch 4 taken
0: branch 5 not taken
236 24: }
237 : }
Generated: 2010-02-10 01:31 by zcov