10b57cec5SDimitry Andric //===----- UninitializedObject.h ---------------------------------*- C++ -*-==// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This file defines helper classes for UninitializedObjectChecker and 100b57cec5SDimitry Andric // documentation about the logic of it. 110b57cec5SDimitry Andric // 120b57cec5SDimitry Andric // The checker reports uninitialized fields in objects created after a 130b57cec5SDimitry Andric // constructor call. 140b57cec5SDimitry Andric // 150b57cec5SDimitry Andric // This checker has several options: 160b57cec5SDimitry Andric // - "Pedantic" (boolean). If its not set or is set to false, the checker 170b57cec5SDimitry Andric // won't emit warnings for objects that don't have at least one initialized 180b57cec5SDimitry Andric // field. This may be set with 190b57cec5SDimitry Andric // 200b57cec5SDimitry Andric // `-analyzer-config optin.cplusplus.UninitializedObject:Pedantic=true`. 210b57cec5SDimitry Andric // 220b57cec5SDimitry Andric // - "NotesAsWarnings" (boolean). If set to true, the checker will emit a 230b57cec5SDimitry Andric // warning for each uninitialized field, as opposed to emitting one warning 240b57cec5SDimitry Andric // per constructor call, and listing the uninitialized fields that belongs 250b57cec5SDimitry Andric // to it in notes. Defaults to false. 260b57cec5SDimitry Andric // 270b57cec5SDimitry Andric // `-analyzer-config \ 280b57cec5SDimitry Andric // optin.cplusplus.UninitializedObject:NotesAsWarnings=true`. 290b57cec5SDimitry Andric // 300b57cec5SDimitry Andric // - "CheckPointeeInitialization" (boolean). If set to false, the checker will 310b57cec5SDimitry Andric // not analyze the pointee of pointer/reference fields, and will only check 320b57cec5SDimitry Andric // whether the object itself is initialized. Defaults to false. 330b57cec5SDimitry Andric // 340b57cec5SDimitry Andric // `-analyzer-config \ 350b57cec5SDimitry Andric // optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true`. 360b57cec5SDimitry Andric // 370b57cec5SDimitry Andric // TODO: With some clever heuristics, some pointers should be dereferenced 380b57cec5SDimitry Andric // by default. For example, if the pointee is constructed within the 390b57cec5SDimitry Andric // constructor call, it's reasonable to say that no external object 400b57cec5SDimitry Andric // references it, and we wouldn't generate multiple report on the same 410b57cec5SDimitry Andric // pointee. 420b57cec5SDimitry Andric // 430b57cec5SDimitry Andric // - "IgnoreRecordsWithField" (string). If supplied, the checker will not 440b57cec5SDimitry Andric // analyze structures that have a field with a name or type name that 450b57cec5SDimitry Andric // matches the given pattern. Defaults to "". 460b57cec5SDimitry Andric // 470b57cec5SDimitry Andric // `-analyzer-config \ 480b57cec5SDimitry Andric // optin.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"`. 490b57cec5SDimitry Andric // 500b57cec5SDimitry Andric // - "IgnoreGuardedFields" (boolean). If set to true, the checker will analyze 510b57cec5SDimitry Andric // _syntactically_ whether the found uninitialized object is used without a 520b57cec5SDimitry Andric // preceding assert call. Defaults to false. 530b57cec5SDimitry Andric // 540b57cec5SDimitry Andric // `-analyzer-config \ 550b57cec5SDimitry Andric // optin.cplusplus.UninitializedObject:IgnoreGuardedFields=true`. 560b57cec5SDimitry Andric // 570b57cec5SDimitry Andric // Most of the following methods as well as the checker itself is defined in 580b57cec5SDimitry Andric // UninitializedObjectChecker.cpp. 590b57cec5SDimitry Andric // 600b57cec5SDimitry Andric // Some methods are implemented in UninitializedPointee.cpp, to reduce the 610b57cec5SDimitry Andric // complexity of the main checker file. 620b57cec5SDimitry Andric // 630b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 640b57cec5SDimitry Andric 650b57cec5SDimitry Andric #ifndef LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H 660b57cec5SDimitry Andric #define LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H 670b57cec5SDimitry Andric 680b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 690b57cec5SDimitry Andric 700b57cec5SDimitry Andric namespace clang { 710b57cec5SDimitry Andric namespace ento { 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric struct UninitObjCheckerOptions { 740b57cec5SDimitry Andric bool IsPedantic = false; 750b57cec5SDimitry Andric bool ShouldConvertNotesToWarnings = false; 760b57cec5SDimitry Andric bool CheckPointeeInitialization = false; 770b57cec5SDimitry Andric std::string IgnoredRecordsWithFieldPattern; 780b57cec5SDimitry Andric bool IgnoreGuardedFields = false; 790b57cec5SDimitry Andric }; 800b57cec5SDimitry Andric 810b57cec5SDimitry Andric /// A lightweight polymorphic wrapper around FieldRegion *. We'll use this 820b57cec5SDimitry Andric /// interface to store addinitional information about fields. As described 830b57cec5SDimitry Andric /// later, a list of these objects (i.e. "fieldchain") will be constructed and 840b57cec5SDimitry Andric /// used for printing note messages should an uninitialized value be found. 850b57cec5SDimitry Andric class FieldNode { 860b57cec5SDimitry Andric protected: 870b57cec5SDimitry Andric const FieldRegion *FR; 880b57cec5SDimitry Andric 890b57cec5SDimitry Andric /// FieldNodes are never meant to be created on the heap, see 900b57cec5SDimitry Andric /// FindUninitializedFields::addFieldToUninits(). 910b57cec5SDimitry Andric /* non-virtual */ ~FieldNode() = default; 920b57cec5SDimitry Andric 930b57cec5SDimitry Andric public: 940b57cec5SDimitry Andric FieldNode(const FieldRegion *FR) : FR(FR) {} 950b57cec5SDimitry Andric 960b57cec5SDimitry Andric // We'll delete all of these special member functions to force the users of 970b57cec5SDimitry Andric // this interface to only store references to FieldNode objects in containers. 980b57cec5SDimitry Andric FieldNode() = delete; 990b57cec5SDimitry Andric FieldNode(const FieldNode &) = delete; 1000b57cec5SDimitry Andric FieldNode(FieldNode &&) = delete; 1010b57cec5SDimitry Andric FieldNode &operator=(const FieldNode &) = delete; 1020b57cec5SDimitry Andric FieldNode &operator=(const FieldNode &&) = delete; 1030b57cec5SDimitry Andric 1040b57cec5SDimitry Andric void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(this); } 1050b57cec5SDimitry Andric 1060b57cec5SDimitry Andric /// Helper method for uniqueing. 1070b57cec5SDimitry Andric bool isSameRegion(const FieldRegion *OtherFR) const { 1080b57cec5SDimitry Andric // Special FieldNode descendants may wrap nullpointers (for example if they 1090b57cec5SDimitry Andric // describe a special relationship between two elements of the fieldchain) 1100b57cec5SDimitry Andric // -- we wouldn't like to unique these objects. 1110b57cec5SDimitry Andric if (FR == nullptr) 1120b57cec5SDimitry Andric return false; 1130b57cec5SDimitry Andric 1140b57cec5SDimitry Andric return FR == OtherFR; 1150b57cec5SDimitry Andric } 1160b57cec5SDimitry Andric 1170b57cec5SDimitry Andric const FieldRegion *getRegion() const { return FR; } 1180b57cec5SDimitry Andric const FieldDecl *getDecl() const { 1190b57cec5SDimitry Andric assert(FR); 1200b57cec5SDimitry Andric return FR->getDecl(); 1210b57cec5SDimitry Andric } 1220b57cec5SDimitry Andric 1230b57cec5SDimitry Andric // When a fieldchain is printed, it will have the following format (without 1240b57cec5SDimitry Andric // newline, indices are in order of insertion, from 1 to n): 1250b57cec5SDimitry Andric // 1260b57cec5SDimitry Andric // <note_message_n>'<prefix_n><prefix_n-1>...<prefix_1> 1270b57cec5SDimitry Andric // this-><node_1><separator_1><node_2><separator_2>...<node_n>' 1280b57cec5SDimitry Andric 1290b57cec5SDimitry Andric /// If this is the last element of the fieldchain, this method will print the 1300b57cec5SDimitry Andric /// note message associated with it. 1310b57cec5SDimitry Andric /// The note message should state something like "uninitialized field" or 1320b57cec5SDimitry Andric /// "uninitialized pointee" etc. 1330b57cec5SDimitry Andric virtual void printNoteMsg(llvm::raw_ostream &Out) const = 0; 1340b57cec5SDimitry Andric 1350b57cec5SDimitry Andric /// Print any prefixes before the fieldchain. Could contain casts, etc. 1360b57cec5SDimitry Andric virtual void printPrefix(llvm::raw_ostream &Out) const = 0; 1370b57cec5SDimitry Andric 1380b57cec5SDimitry Andric /// Print the node. Should contain the name of the field stored in FR. 1390b57cec5SDimitry Andric virtual void printNode(llvm::raw_ostream &Out) const = 0; 1400b57cec5SDimitry Andric 1410b57cec5SDimitry Andric /// Print the separator. For example, fields may be separated with '.' or 1420b57cec5SDimitry Andric /// "->". 1430b57cec5SDimitry Andric virtual void printSeparator(llvm::raw_ostream &Out) const = 0; 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric virtual bool isBase() const { return false; } 1460b57cec5SDimitry Andric }; 1470b57cec5SDimitry Andric 1480b57cec5SDimitry Andric /// Returns with Field's name. This is a helper function to get the correct name 1490b57cec5SDimitry Andric /// even if Field is a captured lambda variable. 1500b57cec5SDimitry Andric std::string getVariableName(const FieldDecl *Field); 1510b57cec5SDimitry Andric 1520b57cec5SDimitry Andric /// Represents a field chain. A field chain is a list of fields where the first 1530b57cec5SDimitry Andric /// element of the chain is the object under checking (not stored), and every 1540b57cec5SDimitry Andric /// other element is a field, and the element that precedes it is the object 1550b57cec5SDimitry Andric /// that contains it. 1560b57cec5SDimitry Andric /// 1570b57cec5SDimitry Andric /// Note that this class is immutable (essentially a wrapper around an 1580b57cec5SDimitry Andric /// ImmutableList), new FieldChainInfo objects may be created by member 1590b57cec5SDimitry Andric /// functions such as add() and replaceHead(). 1600b57cec5SDimitry Andric class FieldChainInfo { 1610b57cec5SDimitry Andric public: 1620b57cec5SDimitry Andric using FieldChain = llvm::ImmutableList<const FieldNode &>; 1630b57cec5SDimitry Andric 1640b57cec5SDimitry Andric private: 1650b57cec5SDimitry Andric FieldChain::Factory &ChainFactory; 1660b57cec5SDimitry Andric FieldChain Chain; 1670b57cec5SDimitry Andric 1680b57cec5SDimitry Andric FieldChainInfo(FieldChain::Factory &F, FieldChain NewChain) 1690b57cec5SDimitry Andric : FieldChainInfo(F) { 1700b57cec5SDimitry Andric Chain = NewChain; 1710b57cec5SDimitry Andric } 1720b57cec5SDimitry Andric 1730b57cec5SDimitry Andric public: 1740b57cec5SDimitry Andric FieldChainInfo() = delete; 1750b57cec5SDimitry Andric FieldChainInfo(FieldChain::Factory &F) : ChainFactory(F) {} 1760b57cec5SDimitry Andric FieldChainInfo(const FieldChainInfo &Other) = default; 1770b57cec5SDimitry Andric 1780b57cec5SDimitry Andric /// Constructs a new FieldChainInfo object with \p FN appended. 1790b57cec5SDimitry Andric template <class FieldNodeT> FieldChainInfo add(const FieldNodeT &FN); 1800b57cec5SDimitry Andric 1810b57cec5SDimitry Andric /// Constructs a new FieldChainInfo object with \p FN as the new head of the 1820b57cec5SDimitry Andric /// list. 1830b57cec5SDimitry Andric template <class FieldNodeT> FieldChainInfo replaceHead(const FieldNodeT &FN); 1840b57cec5SDimitry Andric 1850b57cec5SDimitry Andric bool contains(const FieldRegion *FR) const; 1860b57cec5SDimitry Andric bool isEmpty() const { return Chain.isEmpty(); } 1870b57cec5SDimitry Andric 1880b57cec5SDimitry Andric const FieldNode &getHead() const { return Chain.getHead(); } 1890b57cec5SDimitry Andric const FieldRegion *getUninitRegion() const { return getHead().getRegion(); } 1900b57cec5SDimitry Andric 1910b57cec5SDimitry Andric void printNoteMsg(llvm::raw_ostream &Out) const; 1920b57cec5SDimitry Andric }; 1930b57cec5SDimitry Andric 1940b57cec5SDimitry Andric using UninitFieldMap = std::map<const FieldRegion *, llvm::SmallString<50>>; 1950b57cec5SDimitry Andric 1960b57cec5SDimitry Andric /// Searches for and stores uninitialized fields in a non-union object. 1970b57cec5SDimitry Andric class FindUninitializedFields { 1980b57cec5SDimitry Andric ProgramStateRef State; 1990b57cec5SDimitry Andric const TypedValueRegion *const ObjectR; 2000b57cec5SDimitry Andric 2010b57cec5SDimitry Andric const UninitObjCheckerOptions Opts; 2020b57cec5SDimitry Andric bool IsAnyFieldInitialized = false; 2030b57cec5SDimitry Andric 2040b57cec5SDimitry Andric FieldChainInfo::FieldChain::Factory ChainFactory; 2050b57cec5SDimitry Andric 2060b57cec5SDimitry Andric /// A map for assigning uninitialized regions to note messages. For example, 2070b57cec5SDimitry Andric /// 2080b57cec5SDimitry Andric /// struct A { 2090b57cec5SDimitry Andric /// int x; 2100b57cec5SDimitry Andric /// }; 2110b57cec5SDimitry Andric /// 2120b57cec5SDimitry Andric /// A a; 2130b57cec5SDimitry Andric /// 2140b57cec5SDimitry Andric /// After analyzing `a`, the map will contain a pair for `a.x`'s region and 2150b57cec5SDimitry Andric /// the note message "uninitialized field 'this->x'. 2160b57cec5SDimitry Andric UninitFieldMap UninitFields; 2170b57cec5SDimitry Andric 2180b57cec5SDimitry Andric public: 2190b57cec5SDimitry Andric /// Constructs the FindUninitializedField object, searches for and stores 2200b57cec5SDimitry Andric /// uninitialized fields in R. 2210b57cec5SDimitry Andric FindUninitializedFields(ProgramStateRef State, 2220b57cec5SDimitry Andric const TypedValueRegion *const R, 2230b57cec5SDimitry Andric const UninitObjCheckerOptions &Opts); 2240b57cec5SDimitry Andric 2250b57cec5SDimitry Andric /// Returns with the modified state and a map of (uninitialized region, 2260b57cec5SDimitry Andric /// note message) pairs. 2270b57cec5SDimitry Andric std::pair<ProgramStateRef, const UninitFieldMap &> getResults() { 2280b57cec5SDimitry Andric return {State, UninitFields}; 2290b57cec5SDimitry Andric } 2300b57cec5SDimitry Andric 2310b57cec5SDimitry Andric /// Returns whether the analyzed region contains at least one initialized 2320b57cec5SDimitry Andric /// field. Note that this includes subfields as well, not just direct ones, 2330b57cec5SDimitry Andric /// and will return false if an uninitialized pointee is found with 2340b57cec5SDimitry Andric /// CheckPointeeInitialization enabled. 2350b57cec5SDimitry Andric bool isAnyFieldInitialized() { return IsAnyFieldInitialized; } 2360b57cec5SDimitry Andric 2370b57cec5SDimitry Andric private: 2380b57cec5SDimitry Andric // For the purposes of this checker, we'll regard the analyzed region as a 2390b57cec5SDimitry Andric // directed tree, where 2400b57cec5SDimitry Andric // * the root is the object under checking 2410b57cec5SDimitry Andric // * every node is an object that is 2420b57cec5SDimitry Andric // - a union 2430b57cec5SDimitry Andric // - a non-union record 2440b57cec5SDimitry Andric // - dereferenceable (see isDereferencableType()) 2450b57cec5SDimitry Andric // - an array 2460b57cec5SDimitry Andric // - of a primitive type (see isPrimitiveType()) 2470b57cec5SDimitry Andric // * the parent of each node is the object that contains it 2480b57cec5SDimitry Andric // * every leaf is an array, a primitive object, a nullptr or an undefined 2490b57cec5SDimitry Andric // pointer. 2500b57cec5SDimitry Andric // 2510b57cec5SDimitry Andric // Example: 2520b57cec5SDimitry Andric // 2530b57cec5SDimitry Andric // struct A { 2540b57cec5SDimitry Andric // struct B { 2550b57cec5SDimitry Andric // int x, y = 0; 2560b57cec5SDimitry Andric // }; 2570b57cec5SDimitry Andric // B b; 2580b57cec5SDimitry Andric // int *iptr = new int; 2590b57cec5SDimitry Andric // B* bptr; 2600b57cec5SDimitry Andric // 2610b57cec5SDimitry Andric // A() {} 2620b57cec5SDimitry Andric // }; 2630b57cec5SDimitry Andric // 2640b57cec5SDimitry Andric // The directed tree: 2650b57cec5SDimitry Andric // 2660b57cec5SDimitry Andric // ->x 2670b57cec5SDimitry Andric // / 2680b57cec5SDimitry Andric // ->b--->y 2690b57cec5SDimitry Andric // / 2700b57cec5SDimitry Andric // A-->iptr->(int value) 2710b57cec5SDimitry Andric // \ 2720b57cec5SDimitry Andric // ->bptr 2730b57cec5SDimitry Andric // 2740b57cec5SDimitry Andric // From this we'll construct a vector of fieldchains, where each fieldchain 2750b57cec5SDimitry Andric // represents an uninitialized field. An uninitialized field may be a 2760b57cec5SDimitry Andric // primitive object, a pointer, a pointee or a union without a single 2770b57cec5SDimitry Andric // initialized field. 2780b57cec5SDimitry Andric // In the above example, for the default constructor call we'll end up with 2790b57cec5SDimitry Andric // these fieldchains: 2800b57cec5SDimitry Andric // 2810b57cec5SDimitry Andric // this->b.x 2820b57cec5SDimitry Andric // this->iptr (pointee uninit) 2830b57cec5SDimitry Andric // this->bptr (pointer uninit) 2840b57cec5SDimitry Andric // 2850b57cec5SDimitry Andric // We'll traverse each node of the above graph with the appropriate one of 2860b57cec5SDimitry Andric // these methods: 2870b57cec5SDimitry Andric 2880b57cec5SDimitry Andric /// Checks the region of a union object, and returns true if no field is 2890b57cec5SDimitry Andric /// initialized within the region. 2900b57cec5SDimitry Andric bool isUnionUninit(const TypedValueRegion *R); 2910b57cec5SDimitry Andric 2920b57cec5SDimitry Andric /// Checks a region of a non-union object, and returns true if an 2930b57cec5SDimitry Andric /// uninitialized field is found within the region. 2940b57cec5SDimitry Andric bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain); 2950b57cec5SDimitry Andric 2960b57cec5SDimitry Andric /// Checks a region of a pointer or reference object, and returns true if the 2970b57cec5SDimitry Andric /// ptr/ref object itself or any field within the pointee's region is 2980b57cec5SDimitry Andric /// uninitialized. 2990b57cec5SDimitry Andric bool isDereferencableUninit(const FieldRegion *FR, FieldChainInfo LocalChain); 3000b57cec5SDimitry Andric 3010b57cec5SDimitry Andric /// Returns true if the value of a primitive object is uninitialized. 302*647cbc5dSDimitry Andric bool isPrimitiveUninit(SVal V); 3030b57cec5SDimitry Andric 3040b57cec5SDimitry Andric // Note that we don't have a method for arrays -- the elements of an array are 3050b57cec5SDimitry Andric // often left uninitialized intentionally even when it is of a C++ record 3060b57cec5SDimitry Andric // type, so we'll assume that an array is always initialized. 3070b57cec5SDimitry Andric // TODO: Add a support for nonloc::LocAsInteger. 3080b57cec5SDimitry Andric 3090b57cec5SDimitry Andric /// Processes LocalChain and attempts to insert it into UninitFields. Returns 3100b57cec5SDimitry Andric /// true on success. Also adds the head of the list and \p PointeeR (if 3110b57cec5SDimitry Andric /// supplied) to the GDM as already analyzed objects. 3120b57cec5SDimitry Andric /// 3130b57cec5SDimitry Andric /// Since this class analyzes regions with recursion, we'll only store 3140b57cec5SDimitry Andric /// references to temporary FieldNode objects created on the stack. This means 3150b57cec5SDimitry Andric /// that after analyzing a leaf of the directed tree described above, the 3160b57cec5SDimitry Andric /// elements LocalChain references will be destructed, so we can't store it 3170b57cec5SDimitry Andric /// directly. 3180b57cec5SDimitry Andric bool addFieldToUninits(FieldChainInfo LocalChain, 3190b57cec5SDimitry Andric const MemRegion *PointeeR = nullptr); 3200b57cec5SDimitry Andric }; 3210b57cec5SDimitry Andric 3220b57cec5SDimitry Andric /// Returns true if T is a primitive type. An object of a primitive type only 3230b57cec5SDimitry Andric /// needs to be analyzed as much as checking whether their value is undefined. 3240b57cec5SDimitry Andric inline bool isPrimitiveType(const QualType &T) { 3250b57cec5SDimitry Andric return T->isBuiltinType() || T->isEnumeralType() || 3260b57cec5SDimitry Andric T->isFunctionType() || T->isAtomicType() || 3270b57cec5SDimitry Andric T->isVectorType() || T->isScalarType(); 3280b57cec5SDimitry Andric } 3290b57cec5SDimitry Andric 3300b57cec5SDimitry Andric inline bool isDereferencableType(const QualType &T) { 3310b57cec5SDimitry Andric return T->isAnyPointerType() || T->isReferenceType(); 3320b57cec5SDimitry Andric } 3330b57cec5SDimitry Andric 3340b57cec5SDimitry Andric // Template method definitions. 3350b57cec5SDimitry Andric 3360b57cec5SDimitry Andric template <class FieldNodeT> 3370b57cec5SDimitry Andric inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) { 3380b57cec5SDimitry Andric assert(!contains(FN.getRegion()) && 3390b57cec5SDimitry Andric "Can't add a field that is already a part of the " 3400b57cec5SDimitry Andric "fieldchain! Is this a cyclic reference?"); 3410b57cec5SDimitry Andric 3420b57cec5SDimitry Andric FieldChainInfo NewChain = *this; 3430b57cec5SDimitry Andric NewChain.Chain = ChainFactory.add(FN, Chain); 3440b57cec5SDimitry Andric return NewChain; 3450b57cec5SDimitry Andric } 3460b57cec5SDimitry Andric 3470b57cec5SDimitry Andric template <class FieldNodeT> 3480b57cec5SDimitry Andric inline FieldChainInfo FieldChainInfo::replaceHead(const FieldNodeT &FN) { 3490b57cec5SDimitry Andric FieldChainInfo NewChain(ChainFactory, Chain.getTail()); 3500b57cec5SDimitry Andric return NewChain.add(FN); 3510b57cec5SDimitry Andric } 3520b57cec5SDimitry Andric 3530b57cec5SDimitry Andric } // end of namespace ento 3540b57cec5SDimitry Andric } // end of namespace clang 3550b57cec5SDimitry Andric 3560b57cec5SDimitry Andric #endif // LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H 357