/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===----- UninitializedObjectChecker.cpp ------------------------*- C++ -*-==// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This file defines a checker that reports uninitialized fields in objects |
10 | | // created after a constructor call. |
11 | | // |
12 | | // To read about command line options and how the checker works, refer to the |
13 | | // top of the file and inline comments in UninitializedObject.h. |
14 | | // |
15 | | // Some of the logic is implemented in UninitializedPointee.cpp, to reduce the |
16 | | // complexity of this file. |
17 | | // |
18 | | //===----------------------------------------------------------------------===// |
19 | | |
20 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
21 | | #include "UninitializedObject.h" |
22 | | #include "clang/ASTMatchers/ASTMatchFinder.h" |
23 | | #include "clang/Driver/DriverDiagnostic.h" |
24 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
25 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
26 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
27 | | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" |
28 | | |
29 | | using namespace clang; |
30 | | using namespace clang::ento; |
31 | | using namespace clang::ast_matchers; |
32 | | |
33 | | /// We'll mark fields (and pointee of fields) that are confirmed to be |
34 | | /// uninitialized as already analyzed. |
35 | | REGISTER_SET_WITH_PROGRAMSTATE(AnalyzedRegions, const MemRegion *) |
36 | | |
37 | | namespace { |
38 | | |
39 | | class UninitializedObjectChecker |
40 | | : public Checker<check::EndFunction, check::DeadSymbols> { |
41 | | std::unique_ptr<BugType> BT_uninitField; |
42 | | |
43 | | public: |
44 | | // The fields of this struct will be initialized when registering the checker. |
45 | | UninitObjCheckerOptions Opts; |
46 | | |
47 | | UninitializedObjectChecker() |
48 | 14 | : BT_uninitField(new BugType(this, "Uninitialized fields")) {} |
49 | | |
50 | | void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; |
51 | | void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; |
52 | | }; |
53 | | |
54 | | /// A basic field type, that is not a pointer or a reference, it's dynamic and |
55 | | /// static type is the same. |
56 | | class RegularField final : public FieldNode { |
57 | | public: |
58 | 180 | RegularField(const FieldRegion *FR) : FieldNode(FR) {} |
59 | | |
60 | 128 | void printNoteMsg(llvm::raw_ostream &Out) const override { |
61 | 128 | Out << "uninitialized field "; |
62 | 128 | } |
63 | | |
64 | 158 | void printPrefix(llvm::raw_ostream &Out) const override {} |
65 | | |
66 | 158 | void printNode(llvm::raw_ostream &Out) const override { |
67 | 158 | Out << getVariableName(getDecl()); |
68 | 158 | } |
69 | | |
70 | 30 | void printSeparator(llvm::raw_ostream &Out) const override { Out << '.'; } |
71 | | }; |
72 | | |
73 | | /// Represents that the FieldNode that comes after this is declared in a base |
74 | | /// of the previous FieldNode. As such, this descendant doesn't wrap a |
75 | | /// FieldRegion, and is purely a tool to describe a relation between two other |
76 | | /// FieldRegion wrapping descendants. |
77 | | class BaseClass final : public FieldNode { |
78 | | const QualType BaseClassT; |
79 | | |
80 | | public: |
81 | 80 | BaseClass(const QualType &T) : FieldNode(nullptr), BaseClassT(T) { |
82 | 80 | assert(!T.isNull()); |
83 | 80 | assert(T->getAsCXXRecordDecl()); |
84 | 80 | } |
85 | | |
86 | 0 | void printNoteMsg(llvm::raw_ostream &Out) const override { |
87 | 0 | llvm_unreachable("This node can never be the final node in the " |
88 | 0 | "fieldchain!"); |
89 | 0 | } |
90 | | |
91 | 17 | void printPrefix(llvm::raw_ostream &Out) const override {} |
92 | | |
93 | 17 | void printNode(llvm::raw_ostream &Out) const override { |
94 | 17 | Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::"; |
95 | 17 | } |
96 | | |
97 | 17 | void printSeparator(llvm::raw_ostream &Out) const override {} |
98 | | |
99 | 26 | bool isBase() const override { return true; } |
100 | | }; |
101 | | |
102 | | } // end of anonymous namespace |
103 | | |
104 | | // Utility function declarations. |
105 | | |
106 | | /// Returns the region that was constructed by CtorDecl, or nullptr if that |
107 | | /// isn't possible. |
108 | | static const TypedValueRegion * |
109 | | getConstructedRegion(const CXXConstructorDecl *CtorDecl, |
110 | | CheckerContext &Context); |
111 | | |
112 | | /// Checks whether the object constructed by \p Ctor will be analyzed later |
113 | | /// (e.g. if the object is a field of another object, in which case we'd check |
114 | | /// it multiple times). |
115 | | static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor, |
116 | | CheckerContext &Context); |
117 | | |
118 | | /// Checks whether RD contains a field with a name or type name that matches |
119 | | /// \p Pattern. |
120 | | static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern); |
121 | | |
122 | | /// Checks _syntactically_ whether it is possible to access FD from the record |
123 | | /// that contains it without a preceding assert (even if that access happens |
124 | | /// inside a method). This is mainly used for records that act like unions, like |
125 | | /// having multiple bit fields, with only a fraction being properly initialized. |
126 | | /// If these fields are properly guarded with asserts, this method returns |
127 | | /// false. |
128 | | /// |
129 | | /// Since this check is done syntactically, this method could be inaccurate. |
130 | | static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State); |
131 | | |
132 | | //===----------------------------------------------------------------------===// |
133 | | // Methods for UninitializedObjectChecker. |
134 | | //===----------------------------------------------------------------------===// |
135 | | |
136 | | void UninitializedObjectChecker::checkEndFunction( |
137 | 865 | const ReturnStmt *RS, CheckerContext &Context) const { |
138 | | |
139 | 865 | const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>( |
140 | 865 | Context.getLocationContext()->getDecl()); |
141 | 865 | if (!CtorDecl) |
142 | 370 | return; |
143 | | |
144 | 495 | if (!CtorDecl->isUserProvided()) |
145 | 128 | return; |
146 | | |
147 | 367 | if (CtorDecl->getParent()->isUnion()) |
148 | 8 | return; |
149 | | |
150 | | // This avoids essentially the same error being reported multiple times. |
151 | 359 | if (willObjectBeAnalyzedLater(CtorDecl, Context)) |
152 | 47 | return; |
153 | | |
154 | 312 | const TypedValueRegion *R = getConstructedRegion(CtorDecl, Context); |
155 | 312 | if (!R) |
156 | 29 | return; |
157 | | |
158 | 283 | FindUninitializedFields F(Context.getState(), R, Opts); |
159 | | |
160 | 283 | std::pair<ProgramStateRef, const UninitFieldMap &> UninitInfo = |
161 | 283 | F.getResults(); |
162 | | |
163 | 283 | ProgramStateRef UpdatedState = UninitInfo.first; |
164 | 283 | const UninitFieldMap &UninitFields = UninitInfo.second; |
165 | | |
166 | 283 | if (UninitFields.empty()) { |
167 | 167 | Context.addTransition(UpdatedState); |
168 | 167 | return; |
169 | 167 | } |
170 | | |
171 | | // There are uninitialized fields in the record. |
172 | | |
173 | 116 | ExplodedNode *Node = Context.generateNonFatalErrorNode(UpdatedState); |
174 | 116 | if (!Node) |
175 | 0 | return; |
176 | | |
177 | 116 | PathDiagnosticLocation LocUsedForUniqueing; |
178 | 116 | const Stmt *CallSite = Context.getStackFrame()->getCallSite(); |
179 | 116 | if (CallSite) |
180 | 116 | LocUsedForUniqueing = PathDiagnosticLocation::createBegin( |
181 | 116 | CallSite, Context.getSourceManager(), Node->getLocationContext()); |
182 | | |
183 | | // For Plist consumers that don't support notes just yet, we'll convert notes |
184 | | // to warnings. |
185 | 116 | if (Opts.ShouldConvertNotesToWarnings) { |
186 | 2 | for (const auto &Pair : UninitFields) { |
187 | | |
188 | 2 | auto Report = std::make_unique<PathSensitiveBugReport>( |
189 | 2 | *BT_uninitField, Pair.second, Node, LocUsedForUniqueing, |
190 | 2 | Node->getLocationContext()->getDecl()); |
191 | 2 | Context.emitReport(std::move(Report)); |
192 | 2 | } |
193 | 1 | return; |
194 | 1 | } |
195 | | |
196 | 115 | SmallString<100> WarningBuf; |
197 | 115 | llvm::raw_svector_ostream WarningOS(WarningBuf); |
198 | 115 | WarningOS << UninitFields.size() << " uninitialized field" |
199 | 115 | << (UninitFields.size() == 1 ? ""86 : "s"29 ) |
200 | 115 | << " at the end of the constructor call"; |
201 | | |
202 | 115 | auto Report = std::make_unique<PathSensitiveBugReport>( |
203 | 115 | *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing, |
204 | 115 | Node->getLocationContext()->getDecl()); |
205 | | |
206 | 149 | for (const auto &Pair : UninitFields) { |
207 | 149 | Report->addNote(Pair.second, |
208 | 149 | PathDiagnosticLocation::create(Pair.first->getDecl(), |
209 | 149 | Context.getSourceManager())); |
210 | 149 | } |
211 | 115 | Context.emitReport(std::move(Report)); |
212 | 115 | } |
213 | | |
214 | | void UninitializedObjectChecker::checkDeadSymbols(SymbolReaper &SR, |
215 | 2.51k | CheckerContext &C) const { |
216 | 2.51k | ProgramStateRef State = C.getState(); |
217 | 2.51k | for (const MemRegion *R : State->get<AnalyzedRegions>()) { |
218 | 454 | if (!SR.isLiveRegion(R)) |
219 | 276 | State = State->remove<AnalyzedRegions>(R); |
220 | 454 | } |
221 | 2.51k | } |
222 | | |
223 | | //===----------------------------------------------------------------------===// |
224 | | // Methods for FindUninitializedFields. |
225 | | //===----------------------------------------------------------------------===// |
226 | | |
227 | | FindUninitializedFields::FindUninitializedFields( |
228 | | ProgramStateRef State, const TypedValueRegion *const R, |
229 | | const UninitObjCheckerOptions &Opts) |
230 | 283 | : State(State), ObjectR(R), Opts(Opts) { |
231 | | |
232 | 283 | isNonUnionUninit(ObjectR, FieldChainInfo(ChainFactory)); |
233 | | |
234 | | // In non-pedantic mode, if ObjectR doesn't contain a single initialized |
235 | | // field, we'll assume that Object was intentionally left uninitialized. |
236 | 283 | if (!Opts.IsPedantic && !isAnyFieldInitialized()109 ) |
237 | 13 | UninitFields.clear(); |
238 | 283 | } |
239 | | |
240 | | bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain, |
241 | 196 | const MemRegion *PointeeR) { |
242 | 196 | const FieldRegion *FR = Chain.getUninitRegion(); |
243 | | |
244 | 196 | assert((PointeeR || !isDereferencableType(FR->getDecl()->getType())) && |
245 | 196 | "One must also pass the pointee region as a parameter for " |
246 | 196 | "dereferenceable fields!"); |
247 | | |
248 | 196 | if (State->getStateManager().getContext().getSourceManager().isInSystemHeader( |
249 | 196 | FR->getDecl()->getLocation())) |
250 | 4 | return false; |
251 | | |
252 | 192 | if (Opts.IgnoreGuardedFields && !hasUnguardedAccess(FR->getDecl(), State)16 ) |
253 | 14 | return false; |
254 | | |
255 | 178 | if (State->contains<AnalyzedRegions>(FR)) |
256 | 6 | return false; |
257 | | |
258 | 172 | if (PointeeR) { |
259 | 44 | if (State->contains<AnalyzedRegions>(PointeeR)) { |
260 | 4 | return false; |
261 | 4 | } |
262 | 40 | State = State->add<AnalyzedRegions>(PointeeR); |
263 | 40 | } |
264 | | |
265 | 168 | State = State->add<AnalyzedRegions>(FR); |
266 | | |
267 | 168 | UninitFieldMap::mapped_type NoteMsgBuf; |
268 | 168 | llvm::raw_svector_ostream OS(NoteMsgBuf); |
269 | 168 | Chain.printNoteMsg(OS); |
270 | | |
271 | 168 | return UninitFields.insert({FR, std::move(NoteMsgBuf)}).second; |
272 | 172 | } |
273 | | |
274 | | bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R, |
275 | 444 | FieldChainInfo LocalChain) { |
276 | 444 | assert(R->getValueType()->isRecordType() && |
277 | 444 | !R->getValueType()->isUnionType() && |
278 | 444 | "This method only checks non-union record objects!"); |
279 | | |
280 | 444 | const RecordDecl *RD = R->getValueType()->getAsRecordDecl()->getDefinition(); |
281 | | |
282 | 444 | if (!RD) { |
283 | 2 | IsAnyFieldInitialized = true; |
284 | 2 | return true; |
285 | 2 | } |
286 | | |
287 | 442 | if (!Opts.IgnoredRecordsWithFieldPattern.empty() && |
288 | 442 | shouldIgnoreRecord(RD, Opts.IgnoredRecordsWithFieldPattern)) { |
289 | 5 | IsAnyFieldInitialized = true; |
290 | 5 | return false; |
291 | 5 | } |
292 | | |
293 | 437 | bool ContainsUninitField = false; |
294 | | |
295 | | // Are all of this non-union's fields initialized? |
296 | 665 | for (const FieldDecl *I : RD->fields()) { |
297 | | |
298 | 665 | const auto FieldVal = |
299 | 665 | State->getLValue(I, loc::MemRegionVal(R)).castAs<loc::MemRegionVal>(); |
300 | 665 | const auto *FR = FieldVal.getRegionAs<FieldRegion>(); |
301 | 665 | QualType T = I->getType(); |
302 | | |
303 | | // If LocalChain already contains FR, then we encountered a cyclic |
304 | | // reference. In this case, region FR is already under checking at an |
305 | | // earlier node in the directed tree. |
306 | 665 | if (LocalChain.contains(FR)) |
307 | 4 | return false; |
308 | | |
309 | 661 | if (T->isStructureOrClassType()) { |
310 | 28 | if (isNonUnionUninit(FR, LocalChain.add(RegularField(FR)))) |
311 | 18 | ContainsUninitField = true; |
312 | 28 | continue; |
313 | 28 | } |
314 | | |
315 | 633 | if (T->isUnionType()) { |
316 | 14 | if (isUnionUninit(FR)) { |
317 | 0 | if (addFieldToUninits(LocalChain.add(RegularField(FR)))) |
318 | 0 | ContainsUninitField = true; |
319 | 0 | } else |
320 | 14 | IsAnyFieldInitialized = true; |
321 | 14 | continue; |
322 | 14 | } |
323 | | |
324 | 619 | if (T->isArrayType()) { |
325 | 4 | IsAnyFieldInitialized = true; |
326 | 4 | continue; |
327 | 4 | } |
328 | | |
329 | 615 | SVal V = State->getSVal(FieldVal); |
330 | | |
331 | 615 | if (isDereferencableType(T) || isa<nonloc::LocAsInteger>(V)454 ) { |
332 | 163 | if (isDereferencableUninit(FR, LocalChain)) |
333 | 70 | ContainsUninitField = true; |
334 | 163 | continue; |
335 | 163 | } |
336 | | |
337 | 452 | if (isPrimitiveType(T)) { |
338 | 452 | if (isPrimitiveUninit(V)) { |
339 | 152 | if (addFieldToUninits(LocalChain.add(RegularField(FR)))) |
340 | 128 | ContainsUninitField = true; |
341 | 152 | } |
342 | 452 | continue; |
343 | 452 | } |
344 | | |
345 | 0 | llvm_unreachable("All cases are handled!"); |
346 | 0 | } |
347 | | |
348 | | // Checking bases. The checker will regard inherited data members as direct |
349 | | // fields. |
350 | 433 | const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD); |
351 | 433 | if (!CXXRD) |
352 | 0 | return ContainsUninitField; |
353 | | |
354 | 433 | for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) { |
355 | 80 | const auto *BaseRegion = State->getLValue(BaseSpec, R) |
356 | 80 | .castAs<loc::MemRegionVal>() |
357 | 80 | .getRegionAs<TypedValueRegion>(); |
358 | | |
359 | | // If the head of the list is also a BaseClass, we'll overwrite it to avoid |
360 | | // note messages like 'this->A::B::x'. |
361 | 80 | if (!LocalChain.isEmpty() && LocalChain.getHead().isBase()28 ) { |
362 | 26 | if (isNonUnionUninit(BaseRegion, LocalChain.replaceHead( |
363 | 26 | BaseClass(BaseSpec.getType())))) |
364 | 7 | ContainsUninitField = true; |
365 | 54 | } else { |
366 | 54 | if (isNonUnionUninit(BaseRegion, |
367 | 54 | LocalChain.add(BaseClass(BaseSpec.getType())))) |
368 | 17 | ContainsUninitField = true; |
369 | 54 | } |
370 | 80 | } |
371 | | |
372 | 433 | return ContainsUninitField; |
373 | 433 | } |
374 | | |
375 | 18 | bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) { |
376 | 18 | assert(R->getValueType()->isUnionType() && |
377 | 18 | "This method only checks union objects!"); |
378 | | // TODO: Implement support for union fields. |
379 | 18 | return false; |
380 | 18 | } |
381 | | |
382 | 500 | bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) { |
383 | 500 | if (V.isUndef()) |
384 | 180 | return true; |
385 | | |
386 | 320 | IsAnyFieldInitialized = true; |
387 | 320 | return false; |
388 | 500 | } |
389 | | |
390 | | //===----------------------------------------------------------------------===// |
391 | | // Methods for FieldChainInfo. |
392 | | //===----------------------------------------------------------------------===// |
393 | | |
394 | 1.02k | bool FieldChainInfo::contains(const FieldRegion *FR) const { |
395 | 1.02k | for (const FieldNode &Node : Chain) { |
396 | 356 | if (Node.isSameRegion(FR)) |
397 | 4 | return true; |
398 | 356 | } |
399 | 1.01k | return false; |
400 | 1.02k | } |
401 | | |
402 | | /// Prints every element except the last to `Out`. Since ImmutableLists store |
403 | | /// elements in reverse order, and have no reverse iterators, we use a |
404 | | /// recursive function to print the fieldchain correctly. The last element in |
405 | | /// the chain is to be printed by `FieldChainInfo::print`. |
406 | | static void printTail(llvm::raw_ostream &Out, |
407 | | const FieldChainInfo::FieldChain L); |
408 | | |
409 | | // FIXME: This function constructs an incorrect string in the following case: |
410 | | // |
411 | | // struct Base { int x; }; |
412 | | // struct D1 : Base {}; struct D2 : Base {}; |
413 | | // |
414 | | // struct MostDerived : D1, D2 { |
415 | | // MostDerived() {} |
416 | | // } |
417 | | // |
418 | | // A call to MostDerived::MostDerived() will cause two notes that say |
419 | | // "uninitialized field 'this->x'", but we can't refer to 'x' directly, |
420 | | // we need an explicit namespace resolution whether the uninit field was |
421 | | // 'D1::x' or 'D2::x'. |
422 | 168 | void FieldChainInfo::printNoteMsg(llvm::raw_ostream &Out) const { |
423 | 168 | if (Chain.isEmpty()) |
424 | 0 | return; |
425 | | |
426 | 168 | const FieldNode &LastField = getHead(); |
427 | | |
428 | 168 | LastField.printNoteMsg(Out); |
429 | 168 | Out << '\''; |
430 | | |
431 | 168 | for (const FieldNode &Node : Chain) |
432 | 264 | Node.printPrefix(Out); |
433 | | |
434 | 168 | Out << "this->"; |
435 | 168 | printTail(Out, Chain.getTail()); |
436 | 168 | LastField.printNode(Out); |
437 | 168 | Out << '\''; |
438 | 168 | } |
439 | | |
440 | | static void printTail(llvm::raw_ostream &Out, |
441 | 264 | const FieldChainInfo::FieldChain L) { |
442 | 264 | if (L.isEmpty()) |
443 | 168 | return; |
444 | | |
445 | 96 | printTail(Out, L.getTail()); |
446 | | |
447 | 96 | L.getHead().printNode(Out); |
448 | 96 | L.getHead().printSeparator(Out); |
449 | 96 | } |
450 | | |
451 | | //===----------------------------------------------------------------------===// |
452 | | // Utility functions. |
453 | | //===----------------------------------------------------------------------===// |
454 | | |
455 | | static const TypedValueRegion * |
456 | | getConstructedRegion(const CXXConstructorDecl *CtorDecl, |
457 | 729 | CheckerContext &Context) { |
458 | | |
459 | 729 | Loc ThisLoc = |
460 | 729 | Context.getSValBuilder().getCXXThis(CtorDecl, Context.getStackFrame()); |
461 | | |
462 | 729 | SVal ObjectV = Context.getState()->getSVal(ThisLoc); |
463 | | |
464 | 729 | auto *R = ObjectV.getAsRegion()->getAs<TypedValueRegion>(); |
465 | 729 | if (R && !R->getValueType()->getAsCXXRecordDecl()662 ) |
466 | 0 | return nullptr; |
467 | | |
468 | 729 | return R; |
469 | 729 | } |
470 | | |
471 | | static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor, |
472 | 359 | CheckerContext &Context) { |
473 | | |
474 | 359 | const TypedValueRegion *CurrRegion = getConstructedRegion(Ctor, Context); |
475 | 359 | if (!CurrRegion) |
476 | 29 | return false; |
477 | | |
478 | 330 | const LocationContext *LC = Context.getLocationContext(); |
479 | 615 | while ((LC = LC->getParent())) { |
480 | | |
481 | | // If \p Ctor was called by another constructor. |
482 | 332 | const auto *OtherCtor = dyn_cast<CXXConstructorDecl>(LC->getDecl()); |
483 | 332 | if (!OtherCtor) |
484 | 274 | continue; |
485 | | |
486 | 58 | const TypedValueRegion *OtherRegion = |
487 | 58 | getConstructedRegion(OtherCtor, Context); |
488 | 58 | if (!OtherRegion) |
489 | 9 | continue; |
490 | | |
491 | | // If the CurrRegion is a subregion of OtherRegion, it will be analyzed |
492 | | // during the analysis of OtherRegion. |
493 | 49 | if (CurrRegion->isSubRegionOf(OtherRegion)) |
494 | 47 | return true; |
495 | 49 | } |
496 | | |
497 | 283 | return false; |
498 | 330 | } |
499 | | |
500 | 442 | static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) { |
501 | 442 | llvm::Regex R(Pattern); |
502 | | |
503 | 672 | for (const FieldDecl *FD : RD->fields()) { |
504 | 672 | if (R.match(FD->getType().getAsString())) |
505 | 4 | return true; |
506 | 668 | if (R.match(FD->getName())) |
507 | 1 | return true; |
508 | 668 | } |
509 | | |
510 | 437 | return false; |
511 | 442 | } |
512 | | |
513 | 75 | static const Stmt *getMethodBody(const CXXMethodDecl *M) { |
514 | 75 | if (isa<CXXConstructorDecl>(M)) |
515 | 43 | return nullptr; |
516 | | |
517 | 32 | if (!M->isDefined()) |
518 | 2 | return nullptr; |
519 | | |
520 | 30 | return M->getDefinition()->getBody(); |
521 | 32 | } |
522 | | |
523 | 16 | static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State) { |
524 | | |
525 | 16 | if (FD->getAccess() == AccessSpecifier::AS_public) |
526 | 1 | return true; |
527 | | |
528 | 15 | const auto *Parent = dyn_cast<CXXRecordDecl>(FD->getParent()); |
529 | | |
530 | 15 | if (!Parent) |
531 | 0 | return true; |
532 | | |
533 | 15 | Parent = Parent->getDefinition(); |
534 | 15 | assert(Parent && "The record's definition must be avaible if an uninitialized" |
535 | 15 | " field of it was found!"); |
536 | | |
537 | 15 | ASTContext &AC = State->getStateManager().getContext(); |
538 | | |
539 | 15 | auto FieldAccessM = memberExpr(hasDeclaration(equalsNode(FD))).bind("access"); |
540 | | |
541 | 15 | auto AssertLikeM = callExpr(callee(functionDecl( |
542 | 15 | hasAnyName("exit", "panic", "error", "Assert", "assert", "ziperr", |
543 | 15 | "assfail", "db_error", "__assert", "__assert2", "_wassert", |
544 | 15 | "__assert_rtn", "__assert_fail", "dtrace_assfail", |
545 | 15 | "yy_fatal_error", "_XCAssertionFailureHandler", |
546 | 15 | "_DTAssertionFailureHandler", "_TSAssertionFailureHandler")))); |
547 | | |
548 | 15 | auto NoReturnFuncM = callExpr(callee(functionDecl(isNoReturn()))); |
549 | | |
550 | 15 | auto GuardM = |
551 | 15 | stmt(anyOf(ifStmt(), switchStmt(), conditionalOperator(), AssertLikeM, |
552 | 15 | NoReturnFuncM)) |
553 | 15 | .bind("guard"); |
554 | | |
555 | 75 | for (const CXXMethodDecl *M : Parent->methods()) { |
556 | 75 | const Stmt *MethodBody = getMethodBody(M); |
557 | 75 | if (!MethodBody) |
558 | 45 | continue; |
559 | | |
560 | 30 | auto Accesses = match(stmt(hasDescendant(FieldAccessM)), *MethodBody, AC); |
561 | 30 | if (Accesses.empty()) |
562 | 15 | continue; |
563 | 15 | const auto *FirstAccess = Accesses[0].getNodeAs<MemberExpr>("access"); |
564 | 15 | assert(FirstAccess); |
565 | | |
566 | 15 | auto Guards = match(stmt(hasDescendant(GuardM)), *MethodBody, AC); |
567 | 15 | if (Guards.empty()) |
568 | 1 | return true; |
569 | 14 | const auto *FirstGuard = Guards[0].getNodeAs<Stmt>("guard"); |
570 | 14 | assert(FirstGuard); |
571 | | |
572 | 14 | if (FirstAccess->getBeginLoc() < FirstGuard->getBeginLoc()) |
573 | 0 | return true; |
574 | 14 | } |
575 | | |
576 | 14 | return false; |
577 | 15 | } |
578 | | |
579 | 247 | std::string clang::ento::getVariableName(const FieldDecl *Field) { |
580 | | // If Field is a captured lambda variable, Field->getName() will return with |
581 | | // an empty string. We can however acquire it's name from the lambda's |
582 | | // captures. |
583 | 247 | const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent()); |
584 | | |
585 | 247 | if (CXXParent && CXXParent->isLambda()) { |
586 | 14 | assert(CXXParent->captures_begin()); |
587 | 14 | auto It = CXXParent->captures_begin() + Field->getFieldIndex(); |
588 | | |
589 | 14 | if (It->capturesVariable()) |
590 | 12 | return llvm::Twine("/*captured variable*/" + |
591 | 12 | It->getCapturedVar()->getName()) |
592 | 12 | .str(); |
593 | | |
594 | 2 | if (It->capturesThis()) |
595 | 2 | return "/*'this' capture*/"; |
596 | | |
597 | 0 | llvm_unreachable("No other capture type is expected!"); |
598 | 0 | } |
599 | | |
600 | 233 | return std::string(Field->getName()); |
601 | 247 | } |
602 | | |
603 | 14 | void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) { |
604 | 14 | auto Chk = Mgr.registerChecker<UninitializedObjectChecker>(); |
605 | | |
606 | 14 | const AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions(); |
607 | 14 | UninitObjCheckerOptions &ChOpts = Chk->Opts; |
608 | | |
609 | 14 | ChOpts.IsPedantic = AnOpts.getCheckerBooleanOption(Chk, "Pedantic"); |
610 | 14 | ChOpts.ShouldConvertNotesToWarnings = AnOpts.getCheckerBooleanOption( |
611 | 14 | Chk, "NotesAsWarnings"); |
612 | 14 | ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption( |
613 | 14 | Chk, "CheckPointeeInitialization"); |
614 | 14 | ChOpts.IgnoredRecordsWithFieldPattern = |
615 | 14 | std::string(AnOpts.getCheckerStringOption(Chk, "IgnoreRecordsWithField")); |
616 | 14 | ChOpts.IgnoreGuardedFields = |
617 | 14 | AnOpts.getCheckerBooleanOption(Chk, "IgnoreGuardedFields"); |
618 | | |
619 | 14 | std::string ErrorMsg; |
620 | 14 | if (!llvm::Regex(ChOpts.IgnoredRecordsWithFieldPattern).isValid(ErrorMsg)) |
621 | 1 | Mgr.reportInvalidCheckerOptionValue(Chk, "IgnoreRecordsWithField", |
622 | 1 | "a valid regex, building failed with error message " |
623 | 1 | "\"" + ErrorMsg + "\""); |
624 | 14 | } |
625 | | |
626 | 28 | bool ento::shouldRegisterUninitializedObjectChecker(const CheckerManager &mgr) { |
627 | 28 | return true; |
628 | 28 | } |