156963aecSKristof Umann //===----- UninitializedObjectChecker.cpp ------------------------*- C++ -*-==//
256963aecSKristof Umann //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
656963aecSKristof Umann //
756963aecSKristof Umann //===----------------------------------------------------------------------===//
856963aecSKristof Umann //
956963aecSKristof Umann // This file defines a checker that reports uninitialized fields in objects
1056963aecSKristof Umann // created after a constructor call.
1156963aecSKristof Umann //
126cec6c46SKristof Umann // To read about command line options and how the checker works, refer to the
136cec6c46SKristof Umann // top of the file and inline comments in UninitializedObject.h.
1456963aecSKristof Umann //
1556963aecSKristof Umann // Some of the logic is implemented in UninitializedPointee.cpp, to reduce the
1656963aecSKristof Umann // complexity of this file.
1756963aecSKristof Umann //
1856963aecSKristof Umann //===----------------------------------------------------------------------===//
1956963aecSKristof Umann 
2076a21502SKristof Umann #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
21a37bba47SKristof Umann #include "UninitializedObject.h"
22ffe93a16SKristof Umann #include "clang/ASTMatchers/ASTMatchFinder.h"
23748c139aSKristof Umann #include "clang/Driver/DriverDiagnostic.h"
2456963aecSKristof Umann #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
2556963aecSKristof Umann #include "clang/StaticAnalyzer/Core/Checker.h"
2656963aecSKristof Umann #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
27e4bf456fSCsaba Dabis #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
2856963aecSKristof Umann 
2956963aecSKristof Umann using namespace clang;
3056963aecSKristof Umann using namespace clang::ento;
31ffe93a16SKristof Umann using namespace clang::ast_matchers;
3256963aecSKristof Umann 
334ff77699SKristof Umann /// We'll mark fields (and pointee of fields) that are confirmed to be
344ff77699SKristof Umann /// uninitialized as already analyzed.
354ff77699SKristof Umann REGISTER_SET_WITH_PROGRAMSTATE(AnalyzedRegions, const MemRegion *)
364ff77699SKristof Umann 
3756963aecSKristof Umann namespace {
3856963aecSKristof Umann 
394ff77699SKristof Umann class UninitializedObjectChecker
404ff77699SKristof Umann     : public Checker<check::EndFunction, check::DeadSymbols> {
4156963aecSKristof Umann   std::unique_ptr<BuiltinBug> BT_uninitField;
4256963aecSKristof Umann 
4356963aecSKristof Umann public:
446cec6c46SKristof Umann   // The fields of this struct will be initialized when registering the checker.
456cec6c46SKristof Umann   UninitObjCheckerOptions Opts;
4656963aecSKristof Umann 
UninitializedObjectChecker()4756963aecSKristof Umann   UninitializedObjectChecker()
4856963aecSKristof Umann       : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {}
494ff77699SKristof Umann 
5056963aecSKristof Umann   void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
514ff77699SKristof Umann   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
5256963aecSKristof Umann };
5356963aecSKristof Umann 
54015b0595SKristof Umann /// A basic field type, that is not a pointer or a reference, it's dynamic and
55015b0595SKristof Umann /// static type is the same.
56651d683eSRichard Smith class RegularField final : public FieldNode {
57015b0595SKristof Umann public:
RegularField(const FieldRegion * FR)58015b0595SKristof Umann   RegularField(const FieldRegion *FR) : FieldNode(FR) {}
59015b0595SKristof Umann 
printNoteMsg(llvm::raw_ostream & Out) const60*a210f404SKazu Hirata   void printNoteMsg(llvm::raw_ostream &Out) const override {
61015b0595SKristof Umann     Out << "uninitialized field ";
62015b0595SKristof Umann   }
63015b0595SKristof Umann 
printPrefix(llvm::raw_ostream & Out) const64*a210f404SKazu Hirata   void printPrefix(llvm::raw_ostream &Out) const override {}
65015b0595SKristof Umann 
printNode(llvm::raw_ostream & Out) const66*a210f404SKazu Hirata   void printNode(llvm::raw_ostream &Out) const override {
67015b0595SKristof Umann     Out << getVariableName(getDecl());
68015b0595SKristof Umann   }
69015b0595SKristof Umann 
printSeparator(llvm::raw_ostream & Out) const70*a210f404SKazu Hirata   void printSeparator(llvm::raw_ostream &Out) const override { Out << '.'; }
71015b0595SKristof Umann };
72015b0595SKristof Umann 
73b59b45e7SKristof Umann /// Represents that the FieldNode that comes after this is declared in a base
74ceb5f654SKristof Umann /// of the previous FieldNode. As such, this descendant doesn't wrap a
75ceb5f654SKristof Umann /// FieldRegion, and is purely a tool to describe a relation between two other
76ceb5f654SKristof Umann /// FieldRegion wrapping descendants.
77b59b45e7SKristof Umann class BaseClass final : public FieldNode {
78b59b45e7SKristof Umann   const QualType BaseClassT;
79b59b45e7SKristof Umann 
80b59b45e7SKristof Umann public:
BaseClass(const QualType & T)81b59b45e7SKristof Umann   BaseClass(const QualType &T) : FieldNode(nullptr), BaseClassT(T) {
82b59b45e7SKristof Umann     assert(!T.isNull());
83b59b45e7SKristof Umann     assert(T->getAsCXXRecordDecl());
84b59b45e7SKristof Umann   }
85b59b45e7SKristof Umann 
printNoteMsg(llvm::raw_ostream & Out) const86*a210f404SKazu Hirata   void printNoteMsg(llvm::raw_ostream &Out) const override {
87b59b45e7SKristof Umann     llvm_unreachable("This node can never be the final node in the "
88b59b45e7SKristof Umann                      "fieldchain!");
89b59b45e7SKristof Umann   }
90b59b45e7SKristof Umann 
printPrefix(llvm::raw_ostream & Out) const91*a210f404SKazu Hirata   void printPrefix(llvm::raw_ostream &Out) const override {}
92b59b45e7SKristof Umann 
printNode(llvm::raw_ostream & Out) const93*a210f404SKazu Hirata   void printNode(llvm::raw_ostream &Out) const override {
94b59b45e7SKristof Umann     Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::";
95b59b45e7SKristof Umann   }
96b59b45e7SKristof Umann 
printSeparator(llvm::raw_ostream & Out) const97*a210f404SKazu Hirata   void printSeparator(llvm::raw_ostream &Out) const override {}
98b59b45e7SKristof Umann 
isBase() const99*a210f404SKazu Hirata   bool isBase() const override { return true; }
100b59b45e7SKristof Umann };
101b59b45e7SKristof Umann 
10256963aecSKristof Umann } // end of anonymous namespace
10356963aecSKristof Umann 
10456963aecSKristof Umann // Utility function declarations.
10556963aecSKristof Umann 
106dbabdfacSKristof Umann /// Returns the region that was constructed by CtorDecl, or nullptr if that
107dbabdfacSKristof Umann /// isn't possible.
108dbabdfacSKristof Umann static const TypedValueRegion *
109dbabdfacSKristof Umann getConstructedRegion(const CXXConstructorDecl *CtorDecl,
110dbabdfacSKristof Umann                      CheckerContext &Context);
11156963aecSKristof Umann 
11256963aecSKristof Umann /// Checks whether the object constructed by \p Ctor will be analyzed later
11356963aecSKristof Umann /// (e.g. if the object is a field of another object, in which case we'd check
11456963aecSKristof Umann /// it multiple times).
11556963aecSKristof Umann static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
11656963aecSKristof Umann                                       CheckerContext &Context);
11756963aecSKristof Umann 
118d6145d98SKristof Umann /// Checks whether RD contains a field with a name or type name that matches
119d6145d98SKristof Umann /// \p Pattern.
120d6145d98SKristof Umann static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern);
121d6145d98SKristof Umann 
122ffe93a16SKristof Umann /// Checks _syntactically_ whether it is possible to access FD from the record
123ffe93a16SKristof Umann /// that contains it without a preceding assert (even if that access happens
124ffe93a16SKristof Umann /// inside a method). This is mainly used for records that act like unions, like
125ffe93a16SKristof Umann /// having multiple bit fields, with only a fraction being properly initialized.
126ffe93a16SKristof Umann /// If these fields are properly guarded with asserts, this method returns
127ffe93a16SKristof Umann /// false.
128ffe93a16SKristof Umann ///
129ffe93a16SKristof Umann /// Since this check is done syntactically, this method could be inaccurate.
130ffe93a16SKristof Umann static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State);
131ffe93a16SKristof Umann 
13256963aecSKristof Umann //===----------------------------------------------------------------------===//
13356963aecSKristof Umann //                  Methods for UninitializedObjectChecker.
13456963aecSKristof Umann //===----------------------------------------------------------------------===//
13556963aecSKristof Umann 
checkEndFunction(const ReturnStmt * RS,CheckerContext & Context) const13656963aecSKristof Umann void UninitializedObjectChecker::checkEndFunction(
13756963aecSKristof Umann     const ReturnStmt *RS, CheckerContext &Context) const {
13856963aecSKristof Umann 
13956963aecSKristof Umann   const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>(
14056963aecSKristof Umann       Context.getLocationContext()->getDecl());
14156963aecSKristof Umann   if (!CtorDecl)
14256963aecSKristof Umann     return;
14356963aecSKristof Umann 
14456963aecSKristof Umann   if (!CtorDecl->isUserProvided())
14556963aecSKristof Umann     return;
14656963aecSKristof Umann 
14756963aecSKristof Umann   if (CtorDecl->getParent()->isUnion())
14856963aecSKristof Umann     return;
14956963aecSKristof Umann 
15056963aecSKristof Umann   // This avoids essentially the same error being reported multiple times.
15156963aecSKristof Umann   if (willObjectBeAnalyzedLater(CtorDecl, Context))
15256963aecSKristof Umann     return;
15356963aecSKristof Umann 
154dbabdfacSKristof Umann   const TypedValueRegion *R = getConstructedRegion(CtorDecl, Context);
155dbabdfacSKristof Umann   if (!R)
15656963aecSKristof Umann     return;
15756963aecSKristof Umann 
158dbabdfacSKristof Umann   FindUninitializedFields F(Context.getState(), R, Opts);
15956963aecSKristof Umann 
1604ff77699SKristof Umann   std::pair<ProgramStateRef, const UninitFieldMap &> UninitInfo =
1614ff77699SKristof Umann       F.getResults();
16256963aecSKristof Umann 
1634ff77699SKristof Umann   ProgramStateRef UpdatedState = UninitInfo.first;
1644ff77699SKristof Umann   const UninitFieldMap &UninitFields = UninitInfo.second;
1654ff77699SKristof Umann 
1664ff77699SKristof Umann   if (UninitFields.empty()) {
1674ff77699SKristof Umann     Context.addTransition(UpdatedState);
16856963aecSKristof Umann     return;
1694ff77699SKristof Umann   }
17056963aecSKristof Umann 
17156963aecSKristof Umann   // There are uninitialized fields in the record.
17256963aecSKristof Umann 
1734ff77699SKristof Umann   ExplodedNode *Node = Context.generateNonFatalErrorNode(UpdatedState);
17456963aecSKristof Umann   if (!Node)
17556963aecSKristof Umann     return;
17656963aecSKristof Umann 
17756963aecSKristof Umann   PathDiagnosticLocation LocUsedForUniqueing;
17856963aecSKristof Umann   const Stmt *CallSite = Context.getStackFrame()->getCallSite();
17956963aecSKristof Umann   if (CallSite)
18056963aecSKristof Umann     LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
18156963aecSKristof Umann         CallSite, Context.getSourceManager(), Node->getLocationContext());
18256963aecSKristof Umann 
18356963aecSKristof Umann   // For Plist consumers that don't support notes just yet, we'll convert notes
18456963aecSKristof Umann   // to warnings.
1856cec6c46SKristof Umann   if (Opts.ShouldConvertNotesToWarnings) {
186015b0595SKristof Umann     for (const auto &Pair : UninitFields) {
18756963aecSKristof Umann 
1882f169e7cSArtem Dergachev       auto Report = std::make_unique<PathSensitiveBugReport>(
189015b0595SKristof Umann           *BT_uninitField, Pair.second, Node, LocUsedForUniqueing,
19056963aecSKristof Umann           Node->getLocationContext()->getDecl());
19156963aecSKristof Umann       Context.emitReport(std::move(Report));
19256963aecSKristof Umann     }
19356963aecSKristof Umann     return;
19456963aecSKristof Umann   }
19556963aecSKristof Umann 
19656963aecSKristof Umann   SmallString<100> WarningBuf;
19756963aecSKristof Umann   llvm::raw_svector_ostream WarningOS(WarningBuf);
19856963aecSKristof Umann   WarningOS << UninitFields.size() << " uninitialized field"
19956963aecSKristof Umann             << (UninitFields.size() == 1 ? "" : "s")
20056963aecSKristof Umann             << " at the end of the constructor call";
20156963aecSKristof Umann 
2022f169e7cSArtem Dergachev   auto Report = std::make_unique<PathSensitiveBugReport>(
20356963aecSKristof Umann       *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
20456963aecSKristof Umann       Node->getLocationContext()->getDecl());
20556963aecSKristof Umann 
206015b0595SKristof Umann   for (const auto &Pair : UninitFields) {
207015b0595SKristof Umann     Report->addNote(Pair.second,
208015b0595SKristof Umann                     PathDiagnosticLocation::create(Pair.first->getDecl(),
20956963aecSKristof Umann                                                    Context.getSourceManager()));
21056963aecSKristof Umann   }
21156963aecSKristof Umann   Context.emitReport(std::move(Report));
21256963aecSKristof Umann }
21356963aecSKristof Umann 
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const2144ff77699SKristof Umann void UninitializedObjectChecker::checkDeadSymbols(SymbolReaper &SR,
2154ff77699SKristof Umann                                                   CheckerContext &C) const {
2164ff77699SKristof Umann   ProgramStateRef State = C.getState();
2174ff77699SKristof Umann   for (const MemRegion *R : State->get<AnalyzedRegions>()) {
2184ff77699SKristof Umann     if (!SR.isLiveRegion(R))
2194ff77699SKristof Umann       State = State->remove<AnalyzedRegions>(R);
2204ff77699SKristof Umann   }
2214ff77699SKristof Umann }
2224ff77699SKristof Umann 
22356963aecSKristof Umann //===----------------------------------------------------------------------===//
22456963aecSKristof Umann //                   Methods for FindUninitializedFields.
22556963aecSKristof Umann //===----------------------------------------------------------------------===//
22656963aecSKristof Umann 
FindUninitializedFields(ProgramStateRef State,const TypedValueRegion * const R,const UninitObjCheckerOptions & Opts)22756963aecSKristof Umann FindUninitializedFields::FindUninitializedFields(
22823ca9660SKristof Umann     ProgramStateRef State, const TypedValueRegion *const R,
2296cec6c46SKristof Umann     const UninitObjCheckerOptions &Opts)
2306cec6c46SKristof Umann     : State(State), ObjectR(R), Opts(Opts) {
23156963aecSKristof Umann 
232015b0595SKristof Umann   isNonUnionUninit(ObjectR, FieldChainInfo(ChainFactory));
2336cec6c46SKristof Umann 
2346cec6c46SKristof Umann   // In non-pedantic mode, if ObjectR doesn't contain a single initialized
2356cec6c46SKristof Umann   // field, we'll assume that Object was intentionally left uninitialized.
2366cec6c46SKristof Umann   if (!Opts.IsPedantic && !isAnyFieldInitialized())
2376cec6c46SKristof Umann     UninitFields.clear();
23856963aecSKristof Umann }
23956963aecSKristof Umann 
addFieldToUninits(FieldChainInfo Chain,const MemRegion * PointeeR)2404ff77699SKristof Umann bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,
2414ff77699SKristof Umann                                                 const MemRegion *PointeeR) {
2424ff77699SKristof Umann   const FieldRegion *FR = Chain.getUninitRegion();
2434ff77699SKristof Umann 
2444ff77699SKristof Umann   assert((PointeeR || !isDereferencableType(FR->getDecl()->getType())) &&
2454ff77699SKristof Umann          "One must also pass the pointee region as a parameter for "
246b23ccecbSRaphael Isemann          "dereferenceable fields!");
2474ff77699SKristof Umann 
248ffe93a16SKristof Umann   if (State->getStateManager().getContext().getSourceManager().isInSystemHeader(
249ffe93a16SKristof Umann           FR->getDecl()->getLocation()))
250ffe93a16SKristof Umann     return false;
251ffe93a16SKristof Umann 
252ffe93a16SKristof Umann   if (Opts.IgnoreGuardedFields && !hasUnguardedAccess(FR->getDecl(), State))
253ffe93a16SKristof Umann     return false;
254ffe93a16SKristof Umann 
2554ff77699SKristof Umann   if (State->contains<AnalyzedRegions>(FR))
2564ff77699SKristof Umann     return false;
2574ff77699SKristof Umann 
2584ff77699SKristof Umann   if (PointeeR) {
2594ff77699SKristof Umann     if (State->contains<AnalyzedRegions>(PointeeR)) {
2604ff77699SKristof Umann       return false;
2614ff77699SKristof Umann     }
2624ff77699SKristof Umann     State = State->add<AnalyzedRegions>(PointeeR);
2634ff77699SKristof Umann   }
2644ff77699SKristof Umann 
2654ff77699SKristof Umann   State = State->add<AnalyzedRegions>(FR);
2664ff77699SKristof Umann 
267015b0595SKristof Umann   UninitFieldMap::mapped_type NoteMsgBuf;
268015b0595SKristof Umann   llvm::raw_svector_ostream OS(NoteMsgBuf);
269015b0595SKristof Umann   Chain.printNoteMsg(OS);
270ffe93a16SKristof Umann 
2714ff77699SKristof Umann   return UninitFields.insert({FR, std::move(NoteMsgBuf)}).second;
27256963aecSKristof Umann }
27356963aecSKristof Umann 
isNonUnionUninit(const TypedValueRegion * R,FieldChainInfo LocalChain)27456963aecSKristof Umann bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R,
27556963aecSKristof Umann                                                FieldChainInfo LocalChain) {
27656963aecSKristof Umann   assert(R->getValueType()->isRecordType() &&
27756963aecSKristof Umann          !R->getValueType()->isUnionType() &&
27856963aecSKristof Umann          "This method only checks non-union record objects!");
27956963aecSKristof Umann 
280f0dd1016SKristof Umann   const RecordDecl *RD = R->getValueType()->getAsRecordDecl()->getDefinition();
281f0dd1016SKristof Umann 
282f0dd1016SKristof Umann   if (!RD) {
283f0dd1016SKristof Umann     IsAnyFieldInitialized = true;
284f0dd1016SKristof Umann     return true;
285f0dd1016SKristof Umann   }
28656963aecSKristof Umann 
287d6145d98SKristof Umann   if (!Opts.IgnoredRecordsWithFieldPattern.empty() &&
288d6145d98SKristof Umann       shouldIgnoreRecord(RD, Opts.IgnoredRecordsWithFieldPattern)) {
289d6145d98SKristof Umann     IsAnyFieldInitialized = true;
290d6145d98SKristof Umann     return false;
291d6145d98SKristof Umann   }
292d6145d98SKristof Umann 
29356963aecSKristof Umann   bool ContainsUninitField = false;
29456963aecSKristof Umann 
29556963aecSKristof Umann   // Are all of this non-union's fields initialized?
29656963aecSKristof Umann   for (const FieldDecl *I : RD->fields()) {
29756963aecSKristof Umann 
29856963aecSKristof Umann     const auto FieldVal =
29956963aecSKristof Umann         State->getLValue(I, loc::MemRegionVal(R)).castAs<loc::MemRegionVal>();
30056963aecSKristof Umann     const auto *FR = FieldVal.getRegionAs<FieldRegion>();
30156963aecSKristof Umann     QualType T = I->getType();
30256963aecSKristof Umann 
30356963aecSKristof Umann     // If LocalChain already contains FR, then we encountered a cyclic
30456963aecSKristof Umann     // reference. In this case, region FR is already under checking at an
30556963aecSKristof Umann     // earlier node in the directed tree.
30656963aecSKristof Umann     if (LocalChain.contains(FR))
30756963aecSKristof Umann       return false;
30856963aecSKristof Umann 
30956963aecSKristof Umann     if (T->isStructureOrClassType()) {
310015b0595SKristof Umann       if (isNonUnionUninit(FR, LocalChain.add(RegularField(FR))))
31156963aecSKristof Umann         ContainsUninitField = true;
31256963aecSKristof Umann       continue;
31356963aecSKristof Umann     }
31456963aecSKristof Umann 
31556963aecSKristof Umann     if (T->isUnionType()) {
31656963aecSKristof Umann       if (isUnionUninit(FR)) {
317015b0595SKristof Umann         if (addFieldToUninits(LocalChain.add(RegularField(FR))))
31856963aecSKristof Umann           ContainsUninitField = true;
31956963aecSKristof Umann       } else
32056963aecSKristof Umann         IsAnyFieldInitialized = true;
32156963aecSKristof Umann       continue;
32256963aecSKristof Umann     }
32356963aecSKristof Umann 
32456963aecSKristof Umann     if (T->isArrayType()) {
32556963aecSKristof Umann       IsAnyFieldInitialized = true;
32656963aecSKristof Umann       continue;
32756963aecSKristof Umann     }
32856963aecSKristof Umann 
329f051379fSKristof Umann     SVal V = State->getSVal(FieldVal);
330f051379fSKristof Umann 
33196ccb690SBalazs Benics     if (isDereferencableType(T) || isa<nonloc::LocAsInteger>(V)) {
332ceb5f654SKristof Umann       if (isDereferencableUninit(FR, LocalChain))
33356963aecSKristof Umann         ContainsUninitField = true;
33456963aecSKristof Umann       continue;
33556963aecSKristof Umann     }
33656963aecSKristof Umann 
33756963aecSKristof Umann     if (isPrimitiveType(T)) {
33856963aecSKristof Umann       if (isPrimitiveUninit(V)) {
339015b0595SKristof Umann         if (addFieldToUninits(LocalChain.add(RegularField(FR))))
34056963aecSKristof Umann           ContainsUninitField = true;
34156963aecSKristof Umann       }
34256963aecSKristof Umann       continue;
34356963aecSKristof Umann     }
34456963aecSKristof Umann 
34556963aecSKristof Umann     llvm_unreachable("All cases are handled!");
34656963aecSKristof Umann   }
34756963aecSKristof Umann 
348ceb5f654SKristof Umann   // Checking bases. The checker will regard inherited data members as direct
349ceb5f654SKristof Umann   // fields.
35056963aecSKristof Umann   const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
35156963aecSKristof Umann   if (!CXXRD)
35256963aecSKristof Umann     return ContainsUninitField;
35356963aecSKristof Umann 
35456963aecSKristof Umann   for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) {
35556963aecSKristof Umann     const auto *BaseRegion = State->getLValue(BaseSpec, R)
35656963aecSKristof Umann                                  .castAs<loc::MemRegionVal>()
35756963aecSKristof Umann                                  .getRegionAs<TypedValueRegion>();
35856963aecSKristof Umann 
359b59b45e7SKristof Umann     // If the head of the list is also a BaseClass, we'll overwrite it to avoid
360b59b45e7SKristof Umann     // note messages like 'this->A::B::x'.
361b59b45e7SKristof Umann     if (!LocalChain.isEmpty() && LocalChain.getHead().isBase()) {
362b59b45e7SKristof Umann       if (isNonUnionUninit(BaseRegion, LocalChain.replaceHead(
363b59b45e7SKristof Umann                                            BaseClass(BaseSpec.getType()))))
36456963aecSKristof Umann         ContainsUninitField = true;
365b59b45e7SKristof Umann     } else {
366b59b45e7SKristof Umann       if (isNonUnionUninit(BaseRegion,
367b59b45e7SKristof Umann                            LocalChain.add(BaseClass(BaseSpec.getType()))))
368b59b45e7SKristof Umann         ContainsUninitField = true;
369b59b45e7SKristof Umann     }
37056963aecSKristof Umann   }
37156963aecSKristof Umann 
37256963aecSKristof Umann   return ContainsUninitField;
37356963aecSKristof Umann }
37456963aecSKristof Umann 
isUnionUninit(const TypedValueRegion * R)37556963aecSKristof Umann bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) {
37656963aecSKristof Umann   assert(R->getValueType()->isUnionType() &&
37756963aecSKristof Umann          "This method only checks union objects!");
37856963aecSKristof Umann   // TODO: Implement support for union fields.
37956963aecSKristof Umann   return false;
38056963aecSKristof Umann }
38156963aecSKristof Umann 
isPrimitiveUninit(const SVal & V)38256963aecSKristof Umann bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) {
38356963aecSKristof Umann   if (V.isUndef())
38456963aecSKristof Umann     return true;
38556963aecSKristof Umann 
38656963aecSKristof Umann   IsAnyFieldInitialized = true;
38756963aecSKristof Umann   return false;
38856963aecSKristof Umann }
38956963aecSKristof Umann 
39056963aecSKristof Umann //===----------------------------------------------------------------------===//
39156963aecSKristof Umann //                       Methods for FieldChainInfo.
39256963aecSKristof Umann //===----------------------------------------------------------------------===//
39356963aecSKristof Umann 
contains(const FieldRegion * FR) const394015b0595SKristof Umann bool FieldChainInfo::contains(const FieldRegion *FR) const {
395015b0595SKristof Umann   for (const FieldNode &Node : Chain) {
396015b0595SKristof Umann     if (Node.isSameRegion(FR))
397015b0595SKristof Umann       return true;
39856963aecSKristof Umann   }
399015b0595SKristof Umann   return false;
40056963aecSKristof Umann }
40156963aecSKristof Umann 
402a37bba47SKristof Umann /// Prints every element except the last to `Out`. Since ImmutableLists store
403a37bba47SKristof Umann /// elements in reverse order, and have no reverse iterators, we use a
404a37bba47SKristof Umann /// recursive function to print the fieldchain correctly. The last element in
405ceb5f654SKristof Umann /// the chain is to be printed by `FieldChainInfo::print`.
406a37bba47SKristof Umann static void printTail(llvm::raw_ostream &Out,
40782eeca36SKristof Umann                       const FieldChainInfo::FieldChain L);
408a37bba47SKristof Umann 
409ceb5f654SKristof Umann // FIXME: This function constructs an incorrect string in the following case:
41056963aecSKristof Umann //
41156963aecSKristof Umann //   struct Base { int x; };
41256963aecSKristof Umann //   struct D1 : Base {}; struct D2 : Base {};
41356963aecSKristof Umann //
41456963aecSKristof Umann //   struct MostDerived : D1, D2 {
41556963aecSKristof Umann //     MostDerived() {}
41656963aecSKristof Umann //   }
41756963aecSKristof Umann //
41856963aecSKristof Umann // A call to MostDerived::MostDerived() will cause two notes that say
41956963aecSKristof Umann // "uninitialized field 'this->x'", but we can't refer to 'x' directly,
42056963aecSKristof Umann // we need an explicit namespace resolution whether the uninit field was
42156963aecSKristof Umann // 'D1::x' or 'D2::x'.
printNoteMsg(llvm::raw_ostream & Out) const422015b0595SKristof Umann void FieldChainInfo::printNoteMsg(llvm::raw_ostream &Out) const {
42356963aecSKristof Umann   if (Chain.isEmpty())
42456963aecSKristof Umann     return;
42556963aecSKristof Umann 
42682eeca36SKristof Umann   const FieldNode &LastField = getHead();
427015b0595SKristof Umann 
428015b0595SKristof Umann   LastField.printNoteMsg(Out);
429015b0595SKristof Umann   Out << '\'';
430015b0595SKristof Umann 
431015b0595SKristof Umann   for (const FieldNode &Node : Chain)
432015b0595SKristof Umann     Node.printPrefix(Out);
433015b0595SKristof Umann 
434015b0595SKristof Umann   Out << "this->";
43582eeca36SKristof Umann   printTail(Out, Chain.getTail());
436015b0595SKristof Umann   LastField.printNode(Out);
437015b0595SKristof Umann   Out << '\'';
43856963aecSKristof Umann }
43956963aecSKristof Umann 
printTail(llvm::raw_ostream & Out,const FieldChainInfo::FieldChain L)440a37bba47SKristof Umann static void printTail(llvm::raw_ostream &Out,
44182eeca36SKristof Umann                       const FieldChainInfo::FieldChain L) {
44282eeca36SKristof Umann   if (L.isEmpty())
44356963aecSKristof Umann     return;
44456963aecSKristof Umann 
44582eeca36SKristof Umann   printTail(Out, L.getTail());
446015b0595SKristof Umann 
44782eeca36SKristof Umann   L.getHead().printNode(Out);
44882eeca36SKristof Umann   L.getHead().printSeparator(Out);
44956963aecSKristof Umann }
45056963aecSKristof Umann 
45156963aecSKristof Umann //===----------------------------------------------------------------------===//
45256963aecSKristof Umann //                           Utility functions.
45356963aecSKristof Umann //===----------------------------------------------------------------------===//
45456963aecSKristof Umann 
455dbabdfacSKristof Umann static const TypedValueRegion *
getConstructedRegion(const CXXConstructorDecl * CtorDecl,CheckerContext & Context)456dbabdfacSKristof Umann getConstructedRegion(const CXXConstructorDecl *CtorDecl,
457dbabdfacSKristof Umann                      CheckerContext &Context) {
45856963aecSKristof Umann 
459ffe93a16SKristof Umann   Loc ThisLoc =
460ffe93a16SKristof Umann       Context.getSValBuilder().getCXXThis(CtorDecl, Context.getStackFrame());
46156963aecSKristof Umann 
462dbabdfacSKristof Umann   SVal ObjectV = Context.getState()->getSVal(ThisLoc);
46356963aecSKristof Umann 
464dbabdfacSKristof Umann   auto *R = ObjectV.getAsRegion()->getAs<TypedValueRegion>();
465dbabdfacSKristof Umann   if (R && !R->getValueType()->getAsCXXRecordDecl())
466dbabdfacSKristof Umann     return nullptr;
467dbabdfacSKristof Umann 
468dbabdfacSKristof Umann   return R;
46956963aecSKristof Umann }
47056963aecSKristof Umann 
willObjectBeAnalyzedLater(const CXXConstructorDecl * Ctor,CheckerContext & Context)47156963aecSKristof Umann static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
47256963aecSKristof Umann                                       CheckerContext &Context) {
47356963aecSKristof Umann 
474dbabdfacSKristof Umann   const TypedValueRegion *CurrRegion = getConstructedRegion(Ctor, Context);
475dbabdfacSKristof Umann   if (!CurrRegion)
47656963aecSKristof Umann     return false;
47756963aecSKristof Umann 
47856963aecSKristof Umann   const LocationContext *LC = Context.getLocationContext();
47956963aecSKristof Umann   while ((LC = LC->getParent())) {
48056963aecSKristof Umann 
48156963aecSKristof Umann     // If \p Ctor was called by another constructor.
48256963aecSKristof Umann     const auto *OtherCtor = dyn_cast<CXXConstructorDecl>(LC->getDecl());
48356963aecSKristof Umann     if (!OtherCtor)
48456963aecSKristof Umann       continue;
48556963aecSKristof Umann 
486dbabdfacSKristof Umann     const TypedValueRegion *OtherRegion =
487dbabdfacSKristof Umann         getConstructedRegion(OtherCtor, Context);
488dbabdfacSKristof Umann     if (!OtherRegion)
48956963aecSKristof Umann       continue;
49056963aecSKristof Umann 
491dbabdfacSKristof Umann     // If the CurrRegion is a subregion of OtherRegion, it will be analyzed
492dbabdfacSKristof Umann     // during the analysis of OtherRegion.
493dbabdfacSKristof Umann     if (CurrRegion->isSubRegionOf(OtherRegion))
49456963aecSKristof Umann       return true;
49556963aecSKristof Umann   }
49656963aecSKristof Umann 
49756963aecSKristof Umann   return false;
49856963aecSKristof Umann }
49956963aecSKristof Umann 
shouldIgnoreRecord(const RecordDecl * RD,StringRef Pattern)500d6145d98SKristof Umann static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) {
501d6145d98SKristof Umann   llvm::Regex R(Pattern);
502d6145d98SKristof Umann 
503d6145d98SKristof Umann   for (const FieldDecl *FD : RD->fields()) {
504d6145d98SKristof Umann     if (R.match(FD->getType().getAsString()))
505d6145d98SKristof Umann       return true;
506d6145d98SKristof Umann     if (R.match(FD->getName()))
507d6145d98SKristof Umann       return true;
508d6145d98SKristof Umann   }
509d6145d98SKristof Umann 
510d6145d98SKristof Umann   return false;
511d6145d98SKristof Umann }
512d6145d98SKristof Umann 
getMethodBody(const CXXMethodDecl * M)513ffe93a16SKristof Umann static const Stmt *getMethodBody(const CXXMethodDecl *M) {
514ffe93a16SKristof Umann   if (isa<CXXConstructorDecl>(M))
515ffe93a16SKristof Umann     return nullptr;
516ffe93a16SKristof Umann 
517ffe93a16SKristof Umann   if (!M->isDefined())
518ffe93a16SKristof Umann     return nullptr;
519ffe93a16SKristof Umann 
520ffe93a16SKristof Umann   return M->getDefinition()->getBody();
521ffe93a16SKristof Umann }
522ffe93a16SKristof Umann 
hasUnguardedAccess(const FieldDecl * FD,ProgramStateRef State)523ffe93a16SKristof Umann static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State) {
524ffe93a16SKristof Umann 
525ffe93a16SKristof Umann   if (FD->getAccess() == AccessSpecifier::AS_public)
526ffe93a16SKristof Umann     return true;
527ffe93a16SKristof Umann 
528ffe93a16SKristof Umann   const auto *Parent = dyn_cast<CXXRecordDecl>(FD->getParent());
529ffe93a16SKristof Umann 
530ffe93a16SKristof Umann   if (!Parent)
531ffe93a16SKristof Umann     return true;
532ffe93a16SKristof Umann 
533ffe93a16SKristof Umann   Parent = Parent->getDefinition();
534ffe93a16SKristof Umann   assert(Parent && "The record's definition must be avaible if an uninitialized"
535ffe93a16SKristof Umann                    " field of it was found!");
536ffe93a16SKristof Umann 
537ffe93a16SKristof Umann   ASTContext &AC = State->getStateManager().getContext();
538ffe93a16SKristof Umann 
539ffe93a16SKristof Umann   auto FieldAccessM = memberExpr(hasDeclaration(equalsNode(FD))).bind("access");
540ffe93a16SKristof Umann 
541ffe93a16SKristof Umann   auto AssertLikeM = callExpr(callee(functionDecl(
54241bbb875SNathan James       hasAnyName("exit", "panic", "error", "Assert", "assert", "ziperr",
54341bbb875SNathan James                  "assfail", "db_error", "__assert", "__assert2", "_wassert",
54441bbb875SNathan James                  "__assert_rtn", "__assert_fail", "dtrace_assfail",
54541bbb875SNathan James                  "yy_fatal_error", "_XCAssertionFailureHandler",
54641bbb875SNathan James                  "_DTAssertionFailureHandler", "_TSAssertionFailureHandler"))));
547ffe93a16SKristof Umann 
548ffe93a16SKristof Umann   auto NoReturnFuncM = callExpr(callee(functionDecl(isNoReturn())));
549ffe93a16SKristof Umann 
550ffe93a16SKristof Umann   auto GuardM =
551ffe93a16SKristof Umann       stmt(anyOf(ifStmt(), switchStmt(), conditionalOperator(), AssertLikeM,
552ffe93a16SKristof Umann             NoReturnFuncM))
553ffe93a16SKristof Umann           .bind("guard");
554ffe93a16SKristof Umann 
555ffe93a16SKristof Umann   for (const CXXMethodDecl *M : Parent->methods()) {
556ffe93a16SKristof Umann     const Stmt *MethodBody = getMethodBody(M);
557ffe93a16SKristof Umann     if (!MethodBody)
558ffe93a16SKristof Umann       continue;
559ffe93a16SKristof Umann 
560ffe93a16SKristof Umann     auto Accesses = match(stmt(hasDescendant(FieldAccessM)), *MethodBody, AC);
561ffe93a16SKristof Umann     if (Accesses.empty())
562ffe93a16SKristof Umann       continue;
563ffe93a16SKristof Umann     const auto *FirstAccess = Accesses[0].getNodeAs<MemberExpr>("access");
564ffe93a16SKristof Umann     assert(FirstAccess);
565ffe93a16SKristof Umann 
566ffe93a16SKristof Umann     auto Guards = match(stmt(hasDescendant(GuardM)), *MethodBody, AC);
567ffe93a16SKristof Umann     if (Guards.empty())
568ffe93a16SKristof Umann       return true;
569ffe93a16SKristof Umann     const auto *FirstGuard = Guards[0].getNodeAs<Stmt>("guard");
570ffe93a16SKristof Umann     assert(FirstGuard);
571ffe93a16SKristof Umann 
572ffe93a16SKristof Umann     if (FirstAccess->getBeginLoc() < FirstGuard->getBeginLoc())
573ffe93a16SKristof Umann       return true;
574ffe93a16SKristof Umann   }
575ffe93a16SKristof Umann 
576ffe93a16SKristof Umann   return false;
577ffe93a16SKristof Umann }
578ffe93a16SKristof Umann 
getVariableName(const FieldDecl * Field)579f0dd1016SKristof Umann std::string clang::ento::getVariableName(const FieldDecl *Field) {
58056963aecSKristof Umann   // If Field is a captured lambda variable, Field->getName() will return with
58156963aecSKristof Umann   // an empty string. We can however acquire it's name from the lambda's
58256963aecSKristof Umann   // captures.
58356963aecSKristof Umann   const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent());
58456963aecSKristof Umann 
58556963aecSKristof Umann   if (CXXParent && CXXParent->isLambda()) {
58656963aecSKristof Umann     assert(CXXParent->captures_begin());
58756963aecSKristof Umann     auto It = CXXParent->captures_begin() + Field->getFieldIndex();
588f0dd1016SKristof Umann 
589f0dd1016SKristof Umann     if (It->capturesVariable())
590f0dd1016SKristof Umann       return llvm::Twine("/*captured variable*/" +
591f0dd1016SKristof Umann                          It->getCapturedVar()->getName())
592f0dd1016SKristof Umann           .str();
593f0dd1016SKristof Umann 
594f0dd1016SKristof Umann     if (It->capturesThis())
595f0dd1016SKristof Umann       return "/*'this' capture*/";
596f0dd1016SKristof Umann 
597f0dd1016SKristof Umann     llvm_unreachable("No other capture type is expected!");
59856963aecSKristof Umann   }
59956963aecSKristof Umann 
600adcd0268SBenjamin Kramer   return std::string(Field->getName());
60156963aecSKristof Umann }
60256963aecSKristof Umann 
registerUninitializedObjectChecker(CheckerManager & Mgr)60356963aecSKristof Umann void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
60456963aecSKristof Umann   auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
605ceb5f654SKristof Umann 
6064dc84729SKirstóf Umann   const AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
6076cec6c46SKristof Umann   UninitObjCheckerOptions &ChOpts = Chk->Opts;
6086cec6c46SKristof Umann 
60983cc1b35SKristof Umann   ChOpts.IsPedantic = AnOpts.getCheckerBooleanOption(Chk, "Pedantic");
610088b1c9cSKristof Umann   ChOpts.ShouldConvertNotesToWarnings = AnOpts.getCheckerBooleanOption(
61183cc1b35SKristof Umann       Chk, "NotesAsWarnings");
6120a1f91c8SKristof Umann   ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption(
61383cc1b35SKristof Umann       Chk, "CheckPointeeInitialization");
614d6145d98SKristof Umann   ChOpts.IgnoredRecordsWithFieldPattern =
615adcd0268SBenjamin Kramer       std::string(AnOpts.getCheckerStringOption(Chk, "IgnoreRecordsWithField"));
616ffe93a16SKristof Umann   ChOpts.IgnoreGuardedFields =
61783cc1b35SKristof Umann       AnOpts.getCheckerBooleanOption(Chk, "IgnoreGuardedFields");
618748c139aSKristof Umann 
619748c139aSKristof Umann   std::string ErrorMsg;
620748c139aSKristof Umann   if (!llvm::Regex(ChOpts.IgnoredRecordsWithFieldPattern).isValid(ErrorMsg))
621748c139aSKristof Umann     Mgr.reportInvalidCheckerOptionValue(Chk, "IgnoreRecordsWithField",
622748c139aSKristof Umann         "a valid regex, building failed with error message "
623748c139aSKristof Umann         "\"" + ErrorMsg + "\"");
62456963aecSKristof Umann }
625058a7a45SKristof Umann 
shouldRegisterUninitializedObjectChecker(const CheckerManager & mgr)626bda3dd0dSKirstóf Umann bool ento::shouldRegisterUninitializedObjectChecker(const CheckerManager &mgr) {
627058a7a45SKristof Umann   return true;
628058a7a45SKristof Umann }
629