Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -300,6 +300,10 @@ let ParentPackage = CplusplusAlpha in { +def CtorUninitializedMemberChecker: Checker<"CtorUninitializedMember">, + HelpText<"Reports uninitialized members in constructors">, + DescFile<"CtorUninitializedMemberChecker.cpp">; + def DeleteWithNonVirtualDtorChecker : Checker<"DeleteWithNonVirtualDtor">, HelpText<"Reports destructions of polymorphic objects with a non-virtual " "destructor in their base class">, Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -26,6 +26,7 @@ ClangCheckers.cpp CloneChecker.cpp ConversionChecker.cpp + CtorUninitializedMemberChecker.cpp CXXSelfAssignmentChecker.cpp DeadStoresChecker.cpp DebugCheckers.cpp Index: lib/StaticAnalyzer/Checkers/CtorUninitializedMemberChecker.cpp =================================================================== --- /dev/null +++ lib/StaticAnalyzer/Checkers/CtorUninitializedMemberChecker.cpp @@ -0,0 +1,468 @@ +//=======- CtorUninitializedMemberChecker.cpp --------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a checker that reports uninitialized fields in objects +// created after a constructor call. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include +#include + +using namespace clang; +using namespace clang::ento; + +namespace { + +class CtorUninitializedMemberChecker : public Checker { + std::unique_ptr BT_uninitField; + +public: + CtorUninitializedMemberChecker() + : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {} + void checkEndFunction(CheckerContext &C) const; +}; + +/// Represents a field chain. A field chain is a vector of fields where the +/// first element of the chain is the object under checking (not stored), and +/// every other element is a field, and the element that precedes it is the +/// object that contains it. +class FieldChainInfo { + using FieldChain = llvm::SmallVector; + + FieldChain Chain; + // If this is a fieldchain whose last element is an uninitialized region of a + // pointer type, this variable will store whether the pointer itself or the + // pointee is uninitialized. + bool IsDereferenced = false; + +public: + FieldChainInfo() = default; + FieldChainInfo(const FieldChainInfo &Other, const FieldRegion *FR) + : FieldChainInfo(Other) { + Chain.push_back(FR); + } + + bool isPointer() const { + return Chain.back()->getDecl()->getType()->isPointerType(); + } + bool isDereferenced() const; + void dereference() { IsDereferenced = true; } + const FieldDecl *getEndOfChain() const { return Chain.back()->getDecl(); } + template void toString(SmallString &Buf) const; + friend struct FieldChainInfoComparator; +}; + +struct FieldChainInfoComparator { + bool operator()(const FieldChainInfo &lhs, const FieldChainInfo &rhs) { + return lhs.Chain.back() < rhs.Chain.back(); + } +}; + +using UninitFieldSet = std::set; + +/// Searches for and stores uninitialized fields in a non-union object. +class FindUninitializedFields { + StoreManager &StoreMgr; + MemRegionManager &MrMgr; + Store S; + const TypedValueRegion *const ObjectR; + bool IsChecked = false; + + UninitFieldSet UninitFields; + +public: + FindUninitializedFields(StoreManager &StoreMgr, MemRegionManager &MrMgr, + Store S, const TypedValueRegion *const R); + bool isFullyInitialized(); + const UninitFieldSet &getUninitFields(); + +private: + void addFieldToUninits(FieldChainInfo LocalChain); + + // For the purposes of this checker, we'll regard the object under checking as + // a directed tree, where + // * the root is the object under checking + // * every node is an object that is + // - a union + // - a non-union record + // - a pointer/reference + // - of a fundamental type (int, double, etc.) + // * the parent of each node is the object that contains it + // * every leaf is a fundamental object, a nullptr or an undefined pointer. + // Example: + // + // struct A { + // struct B { + // int x, y = 0; + // }; + // B b; + // int *iptr = new int; + // B* bptr; + // + // A() {} + // }; + // + // The directed tree: + // + // ->x + // / + // ->b--->y + // / + // A-->iptr->(int value) + // \ + // ->bptr + // + // From this we'll construct a vector of fieldchains, where each fieldchain + // represents an uninitialized field. If the field is a pointer type, we'll + // also store whether the pointer itself or value it points to is uninit. + // In the above example, for the default constructor call we'll end up with + // these fieldchains: + // + // this->b.x + // this->iptr (pointed value uninit) + // this->bptr (pointer uninit) + // + // We'll traverse each node of the above graph with the appropiate one of + // these methods: + bool isUnionUninit(const TypedValueRegion *R); + bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain); + bool isPointerOrReferenceUninit(const FieldRegion *FR, + FieldChainInfo LocalChain); + bool isFundamentalUninit(const SVal &V); +}; + +// Utility function declarations. + +/// Returns the with the object that was constructed by CtorDecl, or None if +/// that isn't possible. +Optional +getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context); + +/// Checks whether the constructor under checking is called by another +/// constructor. This avoids essentially the same error being reported multiple +/// times. +bool isCalledByConstructor(const CheckerContext &Context); + +/// Returns true if FD is a void pointer (void*, void**, etc.). +/// We can't know the type of the region that a void pointer points to, so FD +/// can't be analyzed if this function return true for it. +bool isVoidPointer(const FieldDecl *FD); + +} // namespace + +//===----------------------------------------------------------------------===// +// Methods for CtorUninitializedMemberChecker. +//===----------------------------------------------------------------------===// + +void CtorUninitializedMemberChecker::checkEndFunction( + CheckerContext &Context) const { + + const auto *CtorDecl = dyn_cast_or_null( + Context.getLocationContext()->getDecl()); + if (!CtorDecl) + return; + + if (!CtorDecl->isUserProvided()) + return; + + if (CtorDecl->getParent()->isUnion()) + return; + + if (isCalledByConstructor(Context)) + return; + + Optional Object = getObjectVal(CtorDecl, Context); + if (!Object) + return; + + FindUninitializedFields F(Context.getStoreManager(), + Context.getSValBuilder().getRegionManager(), + Object->getStore(), Object->getRegion()); + + if (!F.isFullyInitialized()) + return; + + // There are uninitialized fields in the record. + const UninitFieldSet &UninitFields = F.getUninitFields(); + + ExplodedNode *Node = Context.generateNonFatalErrorNode(Context.getState()); + if (!Node) + return; + + PathDiagnosticLocation LocUsedForUniqueing; + const Stmt *CallSite = Context.getStackFrame()->getCallSite(); + if (CallSite) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin( + CallSite, Context.getSourceManager(), Node->getLocationContext()); + + std::string WarningMsg = std::to_string(UninitFields.size()) + + " uninitialized field" + + (UninitFields.size() == 1 ? "" : "s") + + " at the end of the constructor call"; + + auto Report = llvm::make_unique( + *BT_uninitField, WarningMsg, Node, LocUsedForUniqueing, + Node->getLocationContext()->getDecl()); + + for (const auto &FieldChain : UninitFields) { + SmallString<200> FieldChainBuf; + FieldChain.toString(FieldChainBuf); + SmallString<200> NoteBuf; + llvm::raw_svector_ostream NoteOS(NoteBuf); + + if (FieldChain.isPointer()) { + if (FieldChain.isDereferenced()) + NoteOS << "uninitialized pointee 'this->"; + else + NoteOS << "uninitialized pointer 'this->"; + } else + NoteOS << "uninitialized field 'this->"; + NoteOS << FieldChainBuf; + NoteOS << "'"; + + Report->addNote(NoteBuf, + PathDiagnosticLocation::create(FieldChain.getEndOfChain(), + Context.getSourceManager())); + } + + Context.emitReport(std::move(Report)); +} + +//===----------------------------------------------------------------------===// +// Methods for FindUninitializedFields. +//===----------------------------------------------------------------------===// + +FindUninitializedFields::FindUninitializedFields( + StoreManager &StoreMgr, MemRegionManager &MrMgr, Store S, + const TypedValueRegion *const R) + : StoreMgr(StoreMgr), MrMgr(MrMgr), S(S), ObjectR(R) {} + +bool FindUninitializedFields::isFullyInitialized() { + IsChecked = true; + return isNonUnionUninit(ObjectR, FieldChainInfo()); +} + +const UninitFieldSet &FindUninitializedFields::getUninitFields() { + if (!IsChecked) + isFullyInitialized(); + return UninitFields; +} + +bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R, + FieldChainInfo LocalChain) { + + const RecordDecl *RD = + R->getValueType()->getAs()->getDecl()->getDefinition(); + assert(RD && "Referred record has no definition"); + + bool ContainsUninitField = false; + + // Are all of this non-union's fields initialized? + for (const FieldDecl *I : RD->fields()) { + const FieldRegion *FR = MrMgr.getFieldRegion(I, R); + QualType T = I->getType(); + + if (T->isStructureOrClassType()) { + if (isNonUnionUninit(FR, {LocalChain, FR})) + ContainsUninitField = true; + continue; + } + + if (T->isUnionType()) { + if (isUnionUninit(FR)) { + addFieldToUninits({LocalChain, FR}); + ContainsUninitField = true; + } + continue; + } + + SVal V = StoreMgr.getBinding(S, loc::MemRegionVal(FR)); + + // If this is a pointer or reference type. + if (V.getAs()) { + if (isPointerOrReferenceUninit(FR, {LocalChain, FR})) + ContainsUninitField = true; + continue; + } + + // At this point the field is a fundamental type. + if (isFundamentalUninit(V)) { + addFieldToUninits({LocalChain, FR}); + ContainsUninitField = true; + } + } + + // Checking bases. + const auto *CXXRD = dyn_cast(RD); + if (!CXXRD) + return ContainsUninitField; + + for (const CXXBaseSpecifier BaseSpec : CXXRD->bases()) { + const auto *Base = BaseSpec.getType()->getAsCXXRecordDecl(); + const auto *BaseRegion = + MrMgr.getCXXBaseObjectRegion(Base, R, BaseSpec.isVirtual()); + + if (isNonUnionUninit(BaseRegion, LocalChain)) + ContainsUninitField = true; + } + + return ContainsUninitField; +} + +bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) { + // TODO: Implement support for union fields. + return false; +} + +// Note that pointers/references don't contain fields themselves, so in this +// function we won't add anything to LocalChain. +bool FindUninitializedFields::isPointerOrReferenceUninit( + const FieldRegion *FR, FieldChainInfo LocalChain) { + + SVal V = StoreMgr.getBinding(S, loc::MemRegionVal(FR)); + + if (V.isUnknown() || V.isZeroConstant()) + return false; + + if (V.isUndef()) { + addFieldToUninits(std::move(LocalChain)); + return true; + } + + const FieldDecl *FD = FR->getDecl(); + + if (isVoidPointer(FD)) + return false; + + SVal DerefdV = StoreMgr.getBinding(S, V.castAs()); + while (Optional L = DerefdV.getAs()) + DerefdV = StoreMgr.getBinding(S, *L); + + // If V is a pointer pointing to a record type. + if (Optional RecordV = + DerefdV.getAs()) { + + const TypedValueRegion *RT = RecordV->getRegion(); + + // We can't reason about symbolic regions, assume its initialized. + // Note that this also avoids a potential infinite recursion, because + // constructors for list-like classes are checked without being called, and + // the CSA will conjure a symbolic region for Node *next; or similar code + // snippets. + if (RT->getSymbolicBase()) + return false; + + const auto *T = RT->getValueType()->getAs(); + + if (T->isStructureOrClassType()) + return isNonUnionUninit(RT, std::move(LocalChain)); + + if (T->isUnionType()) { + // TODO: does control ever reach here? + if (isUnionUninit(RT)) { + LocalChain.dereference(); + addFieldToUninits(std::move(LocalChain)); + return true; + } + } + } + + // At this point V is a pointer pointing to a fundamental type. + if (isFundamentalUninit(DerefdV)) { + LocalChain.dereference(); + addFieldToUninits(std::move(LocalChain)); + return true; + } + + return false; +} + +bool FindUninitializedFields::isFundamentalUninit(const SVal &V) { + return V.isUndef(); +} + +void FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain) { + UninitFields.insert(std::move(Chain)); +} + +//===----------------------------------------------------------------------===// +// Methods for FieldChainInfo. +//===----------------------------------------------------------------------===// + +bool FieldChainInfo::isDereferenced() const { + assert(isPointer() && "Only pointers may or may not be dereferenced!"); + return IsDereferenced; +} + +template +void FieldChainInfo::toString(SmallString &Buf) const { + if (Chain.empty()) + return; + + llvm::raw_svector_ostream OS(Buf); + const FieldRegion *Last = Chain.back(); + for (auto It = Chain.begin(); *It != Last; ++It) { + const FieldDecl *Field = (*It)->getDecl(); + OS << Field->getNameAsString(); + OS << (Field->getType()->isPointerType() ? "->" : "."); + } + OS << Last->getDecl()->getNameAsString(); +} + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +namespace { + +bool isVoidPointer(const FieldDecl *FD) { + QualType T = FD->getType(); + while (T->isPointerType() && !T->isVoidPointerType()) { + T = T->getPointeeType(); + } + return T->isVoidPointerType(); +} + +Optional +getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context) { + + Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl->getParent(), + Context.getStackFrame()); + // Getting the value for 'this'. + SVal This = Context.getState()->getSVal(ThisLoc); + + // Getting the value for '*this'. + SVal Object = Context.getState()->getSVal(This.castAs()); + + return Object.getAs(); +} + +bool isCalledByConstructor(const CheckerContext &Context) { + const LocationContext *LC = Context.getLocationContext()->getParent(); + + while (LC) { + if (isa(LC->getDecl())) + return true; + + LC = LC->getParent(); + } + return false; +} + +} // namespace + +void ento::registerCtorUninitializedMemberChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} Index: test/Analysis/ctor-uninitialized-member-inheritance.cpp =================================================================== --- /dev/null +++ test/Analysis/ctor-uninitialized-member-inheritance.cpp @@ -0,0 +1,775 @@ +//RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.CtorUninitializedMember -std=c++11 -verify %s + +//===----------------------------------------------------------------------===// +// Non-polymorphic inheritance tests +//===----------------------------------------------------------------------===// + +class NonPolymorphicLeft1 { + int x; + +protected: + int y; + +public: + NonPolymorphicLeft1() = default; + NonPolymorphicLeft1(int) : x(1) {} +}; + +class NonPolymorphicInheritanceTest1 : public NonPolymorphicLeft1 { + int z; + +public: + NonPolymorphicInheritanceTest1() + : NonPolymorphicLeft1(int{}) { + y = 2; + z = 3; + // All good! + } +}; + +void f00() { + NonPolymorphicInheritanceTest1(); +} + +class NonPolymorphicRight1 { + int x; // expected-note{{uninitialized field 'this->x'}} +protected: + int y; + +public: + NonPolymorphicRight1() = default; + NonPolymorphicRight1(int) : x(4) {} +}; + +class NonPolymorphicInheritanceTest2 : public NonPolymorphicRight1 { + int z; + +public: + NonPolymorphicInheritanceTest2() { + y = 5; + z = 6; // expected-warning{{1 uninitialized field}} + } +}; + +void f01() { + NonPolymorphicInheritanceTest2(); +} + +class NonPolymorphicBaseClass3 { + int x; + +protected: + int y; // expected-note{{uninitialized field 'this->y'}} +public: + NonPolymorphicBaseClass3() = default; + NonPolymorphicBaseClass3(int) : x(7) {} +}; + +class NonPolymorphicInheritanceTest3 : public NonPolymorphicBaseClass3 { + int z; + +public: + NonPolymorphicInheritanceTest3() + : NonPolymorphicBaseClass3(int{}) { + z = 8; // expected-warning{{1 uninitialized field}} + } +}; + +void f02() { + NonPolymorphicInheritanceTest3(); +} + +class NonPolymorphicBaseClass4 { + int x; + +protected: + int y; + +public: + NonPolymorphicBaseClass4() = default; + NonPolymorphicBaseClass4(int) : x(9) {} +}; + +class NonPolymorphicInheritanceTest4 : public NonPolymorphicBaseClass4 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + NonPolymorphicInheritanceTest4() + : NonPolymorphicBaseClass4(int{}) { + y = 10; // expected-warning{{1 uninitialized field}} + } +}; + +void f03() { + NonPolymorphicInheritanceTest4(); +} + +//===----------------------------------------------------------------------===// +// Polymorphic inheritance tests +//===----------------------------------------------------------------------===// + +class PolymorphicLeft1 { + int x; + +protected: + int y; + +public: + virtual ~PolymorphicLeft1() = default; + PolymorphicLeft1() = default; + PolymorphicLeft1(int) : x(11) {} +}; + +class PolymorphicInheritanceTest1 : public PolymorphicLeft1 { + int z; + +public: + PolymorphicInheritanceTest1() + : PolymorphicLeft1(int{}) { + y = 12; + z = 13; + // All good! + } +}; + +void f04() { + PolymorphicInheritanceTest1(); +} + +class PolymorphicRight1 { + int x; // expected-note{{uninitialized field 'this->x'}} +protected: + int y; + +public: + virtual ~PolymorphicRight1() = default; + PolymorphicRight1() = default; + PolymorphicRight1(int) : x(14) {} +}; + +class PolymorphicInheritanceTest2 : public PolymorphicRight1 { + int z; + +public: + PolymorphicInheritanceTest2() { + y = 15; + z = 16; // expected-warning{{1 uninitialized field}} + } +}; + +void f05() { + PolymorphicInheritanceTest2(); +} + +class PolymorphicBaseClass3 { + int x; + +protected: + int y; // expected-note{{uninitialized field 'this->y'}} +public: + virtual ~PolymorphicBaseClass3() = default; + PolymorphicBaseClass3() = default; + PolymorphicBaseClass3(int) : x(17) {} +}; + +class PolymorphicInheritanceTest3 : public PolymorphicBaseClass3 { + int z; + +public: + PolymorphicInheritanceTest3() + : PolymorphicBaseClass3(int{}) { + z = 18; // expected-warning{{1 uninitialized field}} + } +}; + +void f06() { + PolymorphicInheritanceTest3(); +} + +class PolymorphicBaseClass4 { + int x; + +protected: + int y; + +public: + virtual ~PolymorphicBaseClass4() = default; + PolymorphicBaseClass4() = default; + PolymorphicBaseClass4(int) : x(19) {} +}; + +class PolymorphicInheritanceTest4 : public PolymorphicBaseClass4 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + PolymorphicInheritanceTest4() + : PolymorphicBaseClass4(int{}) { + y = 20; // expected-warning{{1 uninitialized field}} + } +}; + +void f07() { + PolymorphicInheritanceTest4(); +} + +//===----------------------------------------------------------------------===// +// Virtual inheritance tests +//===----------------------------------------------------------------------===// + +class VirtualPolymorphicLeft1 { + int x; + +protected: + int y; + +public: + virtual ~VirtualPolymorphicLeft1() = default; + VirtualPolymorphicLeft1() = default; + VirtualPolymorphicLeft1(int) : x(21) {} +}; + +class VirtualInheritanceTest1 : virtual public VirtualPolymorphicLeft1 { + int z; + +public: + VirtualInheritanceTest1() + : VirtualPolymorphicLeft1(int()) { + y = 22; + z = 23; + // All good! + } +}; + +void f08() { + VirtualInheritanceTest1(); +} + +class VirtualPolymorphicRight1 { + int x; // expected-note{{uninitialized field 'this->x'}} +protected: + int y; + +public: + virtual ~VirtualPolymorphicRight1() = default; + VirtualPolymorphicRight1() = default; + VirtualPolymorphicRight1(int) : x(24) {} +}; + +class VirtualInheritanceTest2 : virtual public VirtualPolymorphicRight1 { + int z; + +public: + VirtualInheritanceTest2() { + y = 25; + z = 26; // expected-warning{{1 uninitialized field}} + } +}; + +void f09() { + VirtualInheritanceTest2(); +} + +class VirtualPolymorphicBaseClass3 { + int x; + +protected: + int y; // expected-note{{uninitialized field 'this->y'}} +public: + virtual ~VirtualPolymorphicBaseClass3() = default; + VirtualPolymorphicBaseClass3() = default; + VirtualPolymorphicBaseClass3(int) : x(27) {} +}; + +class VirtualInheritanceTest3 : virtual public VirtualPolymorphicBaseClass3 { + int z; + +public: + VirtualInheritanceTest3() + : VirtualPolymorphicBaseClass3(int{}) { + z = 28; // expected-warning{{1 uninitialized field}} + } +}; + +void f10() { + VirtualInheritanceTest3(); +} + +//===----------------------------------------------------------------------===// +// Multiple inheritance tests +//===----------------------------------------------------------------------===// + +/* + Left Right + \ / + \ / + \ / + MultipleInheritanceTest +*/ + +struct Left1 { + int x; + Left1() = default; + Left1(int) : x(29) {} +}; +struct Right1 { + int y; + Right1() = default; + Right1(int) : y(30) {} +}; + +class MultipleInheritanceTest1 : public Left1, public Right1 { + int z; + +public: + MultipleInheritanceTest1() + : Left1(int{}), + Right1(char{}) { + z = 31; + // All good! + } + + MultipleInheritanceTest1(int) + : Left1(int{}) { + y = 32; + z = 33; + // All good! + } + + MultipleInheritanceTest1(int, int) + : Right1(char{}) { + x = 34; + z = 35; + // All good! + } +}; + +void f11() { + MultipleInheritanceTest1(); + MultipleInheritanceTest1(int()); + MultipleInheritanceTest1(int(), int()); +} + +struct Left2 { + int x; + Left2() = default; + Left2(int) : x(36) {} +}; +struct Right2 { + int y; // expected-note{{uninitialized field 'this->y'}} + Right2() = default; + Right2(int) : y(37) {} +}; + +class MultipleInheritanceTest2 : public Left2, public Right2 { + int z; + +public: + MultipleInheritanceTest2() + : Left2(int{}) { + z = 38; // expected-warning{{1 uninitialized field}} + } +}; + +void f12() { + MultipleInheritanceTest2(); +} + +struct Left3 { + int x; // expected-note{{uninitialized field 'this->x'}} + Left3() = default; + Left3(int) : x(39) {} +}; +struct Right3 { + int y; + Right3() = default; + Right3(int) : y(40) {} +}; + +class MultipleInheritanceTest3 : public Left3, public Right3 { + int z; + +public: + MultipleInheritanceTest3() + : Right3(char{}) { + z = 41; // expected-warning{{1 uninitialized field}} + } +}; + +void f13() { + MultipleInheritanceTest3(); +} + +struct Left4 { + int x; + Left4() = default; + Left4(int) : x(42) {} +}; +struct Right4 { + int y; + Right4() = default; + Right4(int) : y(43) {} +}; + +class MultipleInheritanceTest4 : public Left4, public Right4 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + MultipleInheritanceTest4() + : Left4(int{}), + Right4(char{}) { // expected-warning{{1 uninitialized field}} + } +}; + +void f14() { + MultipleInheritanceTest4(); +} + +struct Left5 { + int x; + Left5() = default; + Left5(int) : x(44) {} +}; +struct Right5 { + int y; // expected-note{{uninitialized field 'this->y'}} + Right5() = default; + Right5(int) : y(45) {} +}; + +class MultipleInheritanceTest5 : public Left5, public Right5 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + MultipleInheritanceTest5() // expected-warning{{2 uninitialized fields}} + : Left5(int{}) { + } +}; + +void f15() { + MultipleInheritanceTest5(); +} + +//===----------------------------------------------------------------------===// +// Non-virtual diamond inheritance tests +//===----------------------------------------------------------------------===// + +/* + NonVirtualBase NonVirtualBase + | | + | | + | | + First Second + \ / + \ / + \ / + NonVirtualDiamondInheritanceTest +*/ + +struct NonVirtualBase1 { + int x; + NonVirtualBase1() = default; + NonVirtualBase1(int) : x(46) {} +}; +struct First1 : public NonVirtualBase1 { + First1() = default; + First1(int) : NonVirtualBase1(int{}) {} +}; +struct Second1 : public NonVirtualBase1 { + Second1() = default; + Second1(int) : NonVirtualBase1(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest1 : public First1, public Second1 { + int z; + +public: + NonVirtualDiamondInheritanceTest1() + : First1(int{}), + Second1(int{}) { + z = 47; + // All good! + } + + NonVirtualDiamondInheritanceTest1(int) + : First1(int{}) { + Second1::x = 48; + z = 49; + // All good! + } + + NonVirtualDiamondInheritanceTest1(int, int) + : Second1(int{}) { + First1::x = 50; + z = 51; + // All good! + } +}; + +void f16() { + NonVirtualDiamondInheritanceTest1(); + NonVirtualDiamondInheritanceTest1(int()); + NonVirtualDiamondInheritanceTest1(int(), int()); +} + +struct NonVirtualBase2 { + int x; // expected-note{{uninitialized field 'this->x'}} + NonVirtualBase2() = default; + NonVirtualBase2(int) : x(52) {} +}; +struct First2 : public NonVirtualBase2 { + First2() = default; + First2(int) : NonVirtualBase2(int{}) {} +}; +struct Second2 : public NonVirtualBase2 { + Second2() = default; + Second2(int) : NonVirtualBase2(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest2 : public First2, public Second2 { + int z; + +public: + NonVirtualDiamondInheritanceTest2() + : First2(int{}) { + z = 53; // expected-warning{{1 uninitialized field}} + } +}; + +void f17() { + NonVirtualDiamondInheritanceTest2(); +} + +struct NonVirtualBase3 { + int x; // expected-note{{uninitialized field 'this->x'}} + NonVirtualBase3() = default; + NonVirtualBase3(int) : x(54) {} +}; +struct First3 : public NonVirtualBase3 { + First3() = default; + First3(int) : NonVirtualBase3(int{}) {} +}; +struct Second3 : public NonVirtualBase3 { + Second3() = default; + Second3(int) : NonVirtualBase3(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest3 : public First3, public Second3 { + int z; + +public: + NonVirtualDiamondInheritanceTest3() + : Second3(int{}) { + z = 55; // expected-warning{{1 uninitialized field}} + } +}; + +void f18() { + NonVirtualDiamondInheritanceTest3(); +} + +struct NonVirtualBase4 { + int x; // expected-note{{uninitialized field 'this->x'}} + // expected-note@-1{{uninitialized field 'this->x'}} + NonVirtualBase4() = default; + NonVirtualBase4(int) : x(56) {} +}; +struct First4 : public NonVirtualBase4 { + First4() = default; + First4(int) : NonVirtualBase4(int{}) {} +}; +struct Second4 : public NonVirtualBase4 { + Second4() = default; + Second4(int) : NonVirtualBase4(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest4 : public First4, public Second4 { + int z; + +public: + NonVirtualDiamondInheritanceTest4() { + z = 57; // expected-warning{{2 uninitialized fields}} + } +}; + +void f19() { + NonVirtualDiamondInheritanceTest4(); +} + +struct NonVirtualBase5 { + int x; + NonVirtualBase5() = default; + NonVirtualBase5(int) : x(58) {} +}; +struct First5 : public NonVirtualBase5 { + First5() = default; + First5(int) : NonVirtualBase5(int{}) {} +}; +struct Second5 : public NonVirtualBase5 { + Second5() = default; + Second5(int) : NonVirtualBase5(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest5 : public First5, public Second5 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + NonVirtualDiamondInheritanceTest5() + : First5(int{}), + Second5(int{}) { // expected-warning{{1 uninitialized field}} + } +}; + +void f20() { + NonVirtualDiamondInheritanceTest5(); +} + +struct NonVirtualBase6 { + int x; // expected-note{{uninitialized field 'this->x'}} + NonVirtualBase6() = default; + NonVirtualBase6(int) : x(59) {} +}; +struct First6 : public NonVirtualBase6 { + First6() = default; + First6(int) : NonVirtualBase6(int{}) {} +}; +struct Second6 : public NonVirtualBase6 { + Second6() = default; + Second6(int) : NonVirtualBase6(int{}) {} +}; + +class NonVirtualDiamondInheritanceTest6 : public First6, public Second6 { + int z; // expected-note{{uninitialized field 'this->z'}} + +public: + NonVirtualDiamondInheritanceTest6() // expected-warning{{2 uninitialized fields}} + : First6(int{}) { + // 'z' and 'Second::x' unintialized + } +}; + +void f21() { + NonVirtualDiamondInheritanceTest6(); +} + +//===----------------------------------------------------------------------===// +// Virtual diamond inheritance tests +//===----------------------------------------------------------------------===// + +/* + VirtualBase + / \ + / \ + / \ + VirtualFirst VirtualSecond + \ / + \ / + \ / + NonVirtualDiamondInheritanceTest +*/ + +struct VirtualBase1 { + int x; + VirtualBase1() = default; + VirtualBase1(int) : x(60) {} +}; +struct VirtualFirst1 : virtual public VirtualBase1 { + VirtualFirst1() = default; + VirtualFirst1(int) : VirtualBase1(int{}) {} + VirtualFirst1(int, int) { x = 61; } +}; +struct VirtualSecond1 : virtual public VirtualBase1 { + VirtualSecond1() = default; + VirtualSecond1(int) : VirtualBase1(int{}) {} + VirtualSecond1(int, int) { x = 62; } +}; + +class VirtualDiamondInheritanceTest1 : public VirtualFirst1, public VirtualSecond1 { + +public: + VirtualDiamondInheritanceTest1() { + x = 0; + // All good! + } + + VirtualDiamondInheritanceTest1(int) + : VirtualFirst1(int{}, int{}), + VirtualSecond1(int{}, int{}) { + // All good! + } + + VirtualDiamondInheritanceTest1(int, int) + : VirtualFirst1(int{}, int{}) { + // All good! + } +}; + +void f22() { + VirtualDiamondInheritanceTest1(); + VirtualDiamondInheritanceTest1(int()); + VirtualDiamondInheritanceTest1(int(), int()); +} + +struct VirtualBase2 { + int x; // expected-note{{uninitialized field 'this->x'}} + VirtualBase2() = default; + VirtualBase2(int) : x(63) {} +}; +struct VirtualFirst2 : virtual public VirtualBase2 { + VirtualFirst2() = default; + VirtualFirst2(int) : VirtualBase2(int{}) {} + VirtualFirst2(int, int) { x = 64; } +}; +struct VirtualSecond2 : virtual public VirtualBase2 { + VirtualSecond2() = default; + VirtualSecond2(int) : VirtualBase2(int{}) {} + VirtualSecond2(int, int) { x = 65; } +}; + +class VirtualDiamondInheritanceTest2 : public VirtualFirst2, public VirtualSecond2 { + +public: + VirtualDiamondInheritanceTest2() // expected-warning{{1 uninitialized field}} + : VirtualFirst2(int{}) { + // From the N4659 C++ Standard Working Draft: + // + // (15.6.2.7) + // [...] A 'mem-initializer' where the 'mem-initializer-id' denotes a + // virtual base class is ignored during execution of a constructor of any + // class that is not the most derived class. + // + // This means that Left1::x will not be initialized, because in both + // VirtualFirst::VirtualFirst(int) and VirtualSecond::VirtualSecond(int) + // the constructor delegation to Left1::Left1(int) will be + // ignored. + } +}; + +void f23() { + VirtualDiamondInheritanceTest2(); +} + +struct VirtualBase3 { + int x; // expected-note{{uninitialized field 'this->x'}} + VirtualBase3() = default; + VirtualBase3(int) : x(66) {} +}; +struct VirtualFirst3 : virtual public VirtualBase3 { + VirtualFirst3() = default; + VirtualFirst3(int) : VirtualBase3(int{}) {} + VirtualFirst3(int, int) { x = 67; } +}; +struct VirtualSecond3 : virtual public VirtualBase3 { + VirtualSecond3() = default; + VirtualSecond3(int) : VirtualBase3(int{}) {} + VirtualSecond3(int, int) { x = 68; } +}; + +class VirtualDiamondInheritanceTest3 : public VirtualFirst3, public VirtualSecond3 { + +public: + VirtualDiamondInheritanceTest3() // expected-warning{{1 uninitialized field}} + : VirtualFirst3(int{}) {} +}; + +void f24() { + VirtualDiamondInheritanceTest3(); +} Index: test/Analysis/ctor-uninitialized-member.cpp =================================================================== --- /dev/null +++ test/Analysis/ctor-uninitialized-member.cpp @@ -0,0 +1,899 @@ +//RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.CtorUninitializedMember -std=c++11 -verify %s + +//===----------------------------------------------------------------------===// +// Default constructor test. +//===----------------------------------------------------------------------===// + +class DefaultConstructorTest { + int a, b, c, d, e, f, g, h, i, j; + +public: + DefaultConstructorTest() = default; +}; + +void f00() { + DefaultConstructorTest(); +} + +//===----------------------------------------------------------------------===// +// Initializer list test. +//===----------------------------------------------------------------------===// + +class InitListTest1 { + int a; + int b; + +public: + InitListTest1() + : a(1), + b(2) { + // All good! + } +}; + +void f01() { + InitListTest1(); +} + +class InitListTest2 { + int a; + int b; // expected-note{{uninitialized field 'this->b'}} + +public: + InitListTest2() + : a(3) {} // expected-warning{{1 uninitialized field}} +}; + +void f02() { + InitListTest2(); +} + +class InitListTest3 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; + +public: + InitListTest3() + : b(4) {} // expected-warning{{1 uninitialized field}} +}; + +void f03() { + InitListTest3(); +} + +//===----------------------------------------------------------------------===// +// Constructor body test. +//===----------------------------------------------------------------------===// + +class CtorBodyTest1 { + int a, b; + +public: + CtorBodyTest1() { + a = 5; + b = 6; + // All good! + } +}; + +void f04() { + CtorBodyTest1(); +} + +class CtorBodyTest2 { + int a; + int b; // expected-note{{uninitialized field 'this->b'}} + +public: + CtorBodyTest2() { + a = 7; // expected-warning{{1 uninitialized field}} + } +}; + +void f05() { + CtorBodyTest2(); +} + +class CtorBodyTest3 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; + +public: + CtorBodyTest3() { + b = 8; // expected-warning{{1 uninitialized field}} + } +}; + +void f06() { + CtorBodyTest3(); +} + +class CtorBodyTest4 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; // expected-note{{uninitialized field 'this->b'}} + +public: + CtorBodyTest4() {} +}; + +void f07() { + CtorBodyTest4(); // expected-warning{{2 uninitialized fields}} +} + +//===----------------------------------------------------------------------===// +// Constructor delegation test. +//===----------------------------------------------------------------------===// + +class CtorDelegationTest1 { + int a; + int b; + +public: + CtorDelegationTest1(int) + : a(9) { + // leaves 'b' unintialized, but we'll never check this function + } + + CtorDelegationTest1() + : CtorDelegationTest1(int{}) { // Initializing 'a' + b = 10; + // All good! + } +}; + +void f08() { + CtorDelegationTest1(); +} + +class CtorDelegationTest2 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; + +public: + CtorDelegationTest2(int) + : b(11) { + // leaves 'a' unintialized, but we'll never check this function + } + + CtorDelegationTest2() + : CtorDelegationTest2(int{}) { // expected-warning{{1 uninitialized field}} + } +}; + +void f09() { + CtorDelegationTest2(); +} + +//===----------------------------------------------------------------------===// +// Tests for classes containing records. +//===----------------------------------------------------------------------===// + +class ContainsRecordTest1 { + struct RecordType { + int x; + int y; + } rec; + int c, d; + +public: + ContainsRecordTest1() + : rec({12, 13}), + c(14), + d(15) { + // All good! + } +}; + +void f10() { + ContainsRecordTest1(); +} + +class ContainsRecordTest2 { + struct RecordType { + int x; + int y; // expected-note{{uninitialized field 'this->rec.y'}} + } rec; + int c, d; + +public: + ContainsRecordTest2() + : c(16), + d(17) { + rec.x = 18; // expected-warning{{1 uninitialized field}} + } +}; + +void f11() { + ContainsRecordTest2(); +} + +class ContainsRecordTest3 { + struct RecordType { + int x; // expected-note{{uninitialized field 'this->rec.x'}} + int y; // expected-note{{uninitialized field 'this->rec.y'}} + } rec; + int c, d; + +public: + ContainsRecordTest3() + : c(19), + d(20) { // expected-warning{{2 uninitialized fields}} + } +}; + +void f12() { + ContainsRecordTest3(); +} + +class ContainsRecordTest4 { + struct RecordType { + int x; // expected-note{{uninitialized field 'this->rec.x'}} + int y; // expected-note{{uninitialized field 'this->rec.y'}} + } rec; + int c, d; // expected-note{{uninitialized field 'this->d'}} + +public: + ContainsRecordTest4() + : c(19) { // expected-warning{{3 uninitialized fields}} + } +}; + +void f12p2() { + ContainsRecordTest4(); +} + +//===----------------------------------------------------------------------===// +// Tests for template classes. +//===----------------------------------------------------------------------===// + +template +class IntTemplateClassTest1 { + T t; + int b; + +public: + IntTemplateClassTest1(T i) { + b = 21; + t = i; + // All good! + } +}; + +void f13() { + IntTemplateClassTest1(22); +} + +template +class IntTemplateClassTest2 { + T t; // expected-note{{uninitialized field 'this->t'}} + int b; + +public: + IntTemplateClassTest2() { + b = 23; // expected-warning{{1 uninitialized field}} + } +}; + +void f14() { + IntTemplateClassTest2(); +} + +struct Record { + int x; // expected-note{{uninitialized field 'this->t.x'}} + int y; // expected-note{{uninitialized field 'this->t.y'}} +}; + +template +class RecordTemplateClassTest { + T t; + int b; + +public: + RecordTemplateClassTest() { + b = 24; // expected-warning{{2 uninitialized fields}} + } +}; + +void f15() { + RecordTemplateClassTest(); +} + +//===----------------------------------------------------------------------===// +// Tests for classes containing pointers. +//===----------------------------------------------------------------------===// + +class NullPtrTest { + struct RecordType { + int x; + int y; + }; + + float *fptr = nullptr; + int *ptr; + RecordType *recPtr; + +public: + NullPtrTest() : ptr(nullptr), recPtr(nullptr) { + // All good! + } +}; + +void f16() { + NullPtrTest(); +} + +class HeapPointerTest1 { + struct RecordType { + // TODO: we'd expect the note: {{uninitialized field 'this->recPtr->y'}} + int x; // no-note + // TODO: we'd expect the note: {{uninitialized field 'this->recPtr->y'}} + int y; // no-note + }; + // TODO: we'd expect the note: {{uninitialized pointee 'this->fptr'}} + float *fptr = new float; // no-note + // TODO: we'd expect the note: {{uninitialized pointee 'this->ptr'}} + int *ptr; // no-note + RecordType *recPtr; + +public: + // TODO: we'd expect the warning: {{4 uninitialized fields}} + HeapPointerTest1() : ptr(new int), recPtr(new RecordType) { // no-note + } +}; + +void f17() { + HeapPointerTest1(); +} + +class HeapPointerTest2 { + struct RecordType { + int x; + int y; + }; + + float *fptr = new float(); // initializes to 0 + int *ptr; + RecordType *recPtr; + +public: + HeapPointerTest2() : ptr(new int{25}), recPtr(new RecordType{26, 27}) { + // All good! + } +}; + +void f18() { + HeapPointerTest2(); +} + +class StackPointerTest1 { +public: + struct RecordType { + int x; + int y; + }; + +private: + int *ptr; + RecordType *recPtr; + +public: + StackPointerTest1(int *_ptr, StackPointerTest1::RecordType *_recPtr) : ptr(_ptr), recPtr(_recPtr) { + // All good! + } +}; + +void f19() { + int ok_a = 28; + StackPointerTest1::RecordType ok_rec{29, 30}; + StackPointerTest1(&ok_a, &ok_rec); // 'a', 'rec.x', 'rec.y' uninitialized +} + +class StackPointerTest2 { +public: + struct RecordType { + int x; // expected-note{{uninitialized field 'this->recPtr->x'}} + int y; // expected-note{{uninitialized field 'this->recPtr->y'}} + }; + +private: + int *ptr; // expected-note{{uninitialized pointee 'this->ptr'}} + RecordType *recPtr; + +public: + StackPointerTest2(int *_ptr, RecordType *_recPtr) : ptr(_ptr), recPtr(_recPtr) { // expected-warning{{3 uninitialized fields}} + } +}; + +void f20() { + int a; + StackPointerTest2::RecordType rec; + StackPointerTest2(&a, &rec); // 'a', 'rec.x', 'rec.y' uninitialized +} + +class UninitPointerTest { + struct RecordType { + int x; + int y; + }; + + int *ptr; // expected-note{{uninitialized pointer 'this->ptr'}} + RecordType *recPtr; + +public: + UninitPointerTest() : recPtr(new RecordType{13, 13}) { // expected-warning{{1 uninitialized field}} + } +}; + +void f21() { + UninitPointerTest(); +} + +class VoidPointerTest { + void *vptr; + +public: + VoidPointerTest(void *vptr, char) : vptr(vptr) { + // All good! + } +}; + +void *malloc(int size); + +void f23() { + void *vptr = malloc(sizeof(int)); + VoidPointerTest(vptr, char()); +} + +class MultiPointerTest1 { +public: + struct RecordType { + int x; + int y; + }; + +private: + RecordType **mptr; // expected-note{{uninitialized pointee 'this->mptr'}} + +public: + MultiPointerTest1(RecordType **p, int) : mptr(p) { // expected-warning{{1 uninitialized field}} + } +}; + +void f25() { + MultiPointerTest1::RecordType *p1; + MultiPointerTest1::RecordType **mptr = &p1; + MultiPointerTest1(mptr, int()); // '*mptr' uninitialized +} + +class MultiPointerTest2 { +public: + struct RecordType { + int x; // expected-note{{uninitialized field 'this->mptr->x'}} + int y; // expected-note{{uninitialized field 'this->mptr->y'}} + }; + +private: + RecordType **mptr; + +public: + MultiPointerTest2(RecordType **p, int) : mptr(p) { // expected-warning{{2 uninitialized fields}} + } +}; + +void f26() { + MultiPointerTest2::RecordType i; + MultiPointerTest2::RecordType *p1 = &i; + MultiPointerTest2::RecordType **mptr = &p1; + MultiPointerTest2(mptr, int()); // '**mptr' uninitialized +} + +class MultiPointerTest3 { +public: + struct RecordType { + int x; + int y; + }; + +private: + RecordType **mptr; + +public: + MultiPointerTest3(RecordType **p, int) : mptr(p) { + // All good! + } +}; + +void f27() { + MultiPointerTest3::RecordType i{31, 32}; + MultiPointerTest3::RecordType *p1 = &i; + MultiPointerTest3::RecordType **mptr = &p1; + MultiPointerTest3(mptr, int()); // '**mptr' uninitialized +} + +class ListTest1 { +public: + struct Node { + Node *next = nullptr; // no crash + int i; + }; + +private: + Node *head = nullptr; + +public: + ListTest1() { + // All good! + } +}; + +void f28() { + ListTest1(); +} + +class ListTest2 { +public: + struct Node { + Node *next = nullptr; + int i; // expected-note{{uninitialized field 'this->head->i'}} + }; + +private: + Node *head = nullptr; + +public: + ListTest2(Node *node, int) : head(node) { // expected-warning{{1 uninitialized field}} + } +}; + +void f29() { + ListTest2::Node n; + ListTest2(&n, int()); +} + +//===----------------------------------------------------------------------===// +// Tests for classes containing references. +//===----------------------------------------------------------------------===// + +class ReferenceTest1 { +public: + struct RecordType { + int x; + int y; + }; + +private: + RecordType &lref; + RecordType &&rref; + +public: + ReferenceTest1(RecordType &lref, RecordType &rref) : lref(lref), rref(static_cast(rref)) { + // All good! + } +}; + +void f30() { + ReferenceTest1::RecordType d{33, 34}; + ReferenceTest1(d, d); +} + +class ReferenceTest2 { +public: + struct RecordType { + int x; // expected-note{{uninitialized field 'this->lref.x'}} + int y; // expected-note{{uninitialized field 'this->lref.y'}} + }; + +private: + RecordType &lref; + RecordType &&rref; + +public: + ReferenceTest2(RecordType &lref, RecordType &rref) + : lref(lref), rref(static_cast(rref)) { // expected-warning{{2 uninitialized fields}} + } +}; + +void f31() { + ReferenceTest2::RecordType c; + ReferenceTest2(c, c); +} + +class ReferenceTest3 { +public: + struct RecordType { + int x; // expected-note{{uninitialized field 'this->lref.x'}} + int y; // expected-note{{uninitialized field 'this->lref.y'}} + }; + +private: + RecordType &lref; + RecordType &&rref; + +public: + ReferenceTest3(RecordType &lref, RecordType &rref) + : lref(lref), rref(static_cast(rref)) { // expected-warning{{2 uninitialized fields}} + } +}; + +void f32() { + ReferenceTest3::RecordType c, d{35, 36}; + ReferenceTest3(c, d); +} + +class ReferenceTest4 { +public: + struct RecordType { + int x; // expected-note{{uninitialized field 'this->rref.x'}} + int y; // expected-note{{uninitialized field 'this->rref.y'}} + }; + +private: + RecordType &lref; + RecordType &&rref; + +public: + ReferenceTest4(RecordType &lref, RecordType &rref) + : lref(lref), rref(static_cast(rref)) { // expected-warning{{2 uninitialized fields}} + } +}; + +void f33() { + ReferenceTest4::RecordType c, d{37, 38}; + ReferenceTest4(d, c); +} + +//===----------------------------------------------------------------------===// +// Tests for classes containing unions. +//===----------------------------------------------------------------------===// + +template +void mayInitialize(T &); + +template +void wontInitialize(const T &); + +class PassingToUnknownFunctionTest1 { + int a, b; + +public: + PassingToUnknownFunctionTest1() { + mayInitialize(a); + mayInitialize(b); + // All good! + } + + PassingToUnknownFunctionTest1(int) { + mayInitialize(a); + // All good! + } + + PassingToUnknownFunctionTest1(int, int) { + mayInitialize(*this); + // All good! + } +}; + +void f34() { + PassingToUnknownFunctionTest1(); + PassingToUnknownFunctionTest1(int()); + PassingToUnknownFunctionTest1(int(), int()); +} + +class PassingToUnknownFunctionTest2 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; + +public: + PassingToUnknownFunctionTest2() { + wontInitialize(a); + b = 4; // expected-warning{{1 uninitialized field}} + } +}; + +void f35() { + PassingToUnknownFunctionTest2(); +} + +//===----------------------------------------------------------------------===// +// Tests for classes containing unions. +//===----------------------------------------------------------------------===// + +// FIXME: As of writing this checker, there is no good support for union types +// in the CSA. Here is non-exhaustive list of cases. +// Note that the rules for unions are different in C++ and C. +// http://lists.llvm.org/pipermail/cfe-dev/2017-March/052910.html + +class ContainsSimpleUnionTest1 { + union SimpleUnion { + float uf; + int ui; + char uc; + } u; + +public: + ContainsSimpleUnionTest1() { + u.uf = 3.14; + // All good! + } +}; + +void f36() { + ContainsSimpleUnionTest1(); +} + +class ContainsSimpleUnionTest2 { + union SimpleUnion { + float uf; + int ui; + char uc; + // TODO: we'd expect the note: {{uninitialized field 'this->u'}} + } u; // no-note + +public: + ContainsSimpleUnionTest2() {} +}; + +void f37() { + // TODO: we'd expect the warning: {{1 uninitialized field}} + ContainsSimpleUnionTest2(); // no-warning +} + +class UnionPointerTest1 { +public: + union SimpleUnion { + float uf; + int ui; + char uc; + }; + +private: + SimpleUnion *uptr; + +public: + UnionPointerTest1(SimpleUnion *uptr, int) : uptr(uptr) { + // All good! + } +}; + +void f38() { + UnionPointerTest1::SimpleUnion u; + u.uf = 41; + UnionPointerTest1(&u, int()); +} + +class UnionPointerTest2 { +public: + union SimpleUnion { + float uf; + int ui; + char uc; + }; + +private: + // TODO: we'd expect the note: {{uninitialized field 'this->uptr'}} + SimpleUnion *uptr; // no-note + +public: + UnionPointerTest2(SimpleUnion *uptr, char) : uptr(uptr) {} +}; + +void f39() { + UnionPointerTest2::SimpleUnion u; + // TODO: we'd expect the warning: {{1 uninitialized field}} + UnionPointerTest2(&u, int()); // no-warning +} + +class ContainsUnionWithRecordTest1 { + union UnionWithRecord { + struct RecordType { + int x; + int y; + } us; + double ud; + long ul; + + UnionWithRecord(){}; + } u; + +public: + ContainsUnionWithRecordTest1() { + u.ud = 3.14; + // All good! + } +}; + +void f40() { + ContainsUnionWithRecordTest1(); +} + +class ContainsUnionWithRecordTest2 { + union UnionWithRecord { + struct RecordType { + int x; + int y; + } us; + double ud; + long ul; + + UnionWithRecord(){}; + } u; + +public: + ContainsUnionWithRecordTest2() { + u.us = UnionWithRecord::RecordType{42, 43}; + // All good! + } +}; + +void f41() { + ContainsUnionWithRecordTest1(); +} + +class ContainsUnionWithRecordTest3 { + union UnionWithRecord { + struct RecordType { + int x; + int y; + } us; + double ud; + long ul; + + UnionWithRecord(){}; + // TODO: we'd expect the note: {{uninitialized field 'this->u'}} + } u; // no-note + +public: + ContainsUnionWithRecordTest3() { + UnionWithRecord::RecordType rec; + rec.x = 44; + // TODO: we'd expect the warning: {{1 uninitialized field}} + u.us = rec; // no-warning + } +}; + +void f42() { + ContainsUnionWithRecordTest3(); +} + +class ContainsUnionWithSimpleUnionTest1 { + union UnionWithSimpleUnion { + union SimpleUnion { + float uf; + int ui; + char uc; + } usu; + long ul; + unsigned uu; + } u; + +public: + ContainsUnionWithSimpleUnionTest1() { + u.usu.ui = 5; + // All good! + } +}; + +void f43() { + ContainsUnionWithSimpleUnionTest1(); +} + +class ContainsUnionWithSimpleUnionTest2 { + union UnionWithSimpleUnion { + union SimpleUnion { + float uf; + int ui; + char uc; + } usu; + long ul; + unsigned uu; + // TODO: we'd expect the note: {{uninitialized field 'this->u'}} + } u; // no-note + +public: + ContainsUnionWithSimpleUnionTest2() {} +}; + +void f44() { + // TODO: we'd expect the warning: {{1 uninitialized field}} + ContainsUnionWithSimpleUnionTest2(); // no-warning +}