Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -314,6 +314,10 @@ "object will be reported">, DescFile<"MisusedMovedObjectChecker.cpp">; +def UninitializedObjectChecker: Checker<"UninitializedObject">, + HelpText<"Reports uninitialized fields after object construction">, + DescFile<"UninitializedObjectChecker.cpp">; + } // end: "alpha.cplusplus" Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -91,6 +91,7 @@ UndefResultChecker.cpp UndefinedArraySubscriptChecker.cpp UndefinedAssignmentChecker.cpp + UninitializedObjectChecker.cpp UnixAPIChecker.cpp UnreachableCodeChecker.cpp VforkChecker.cpp Index: lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp =================================================================== --- /dev/null +++ lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp @@ -0,0 +1,658 @@ +//===----- UninitializedObjectChecker.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. +// +// This checker has an option "Pedantic" (boolean). If its not set or is set to +// false, the checker won't emit warnings for objects that don't have at least +// one initialized field. This may be set with +// `-analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true`. +// +//===----------------------------------------------------------------------===// + +#include "ClangSACheckers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include + +using namespace clang; +using namespace clang::ento; + +namespace { + +class UninitializedObjectChecker : public Checker { + std::unique_ptr BT_uninitField; + +public: + bool IsPedantic; // Will be initialized when registering the checker. + + UninitializedObjectChecker() + : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {} + void checkEndFunction(CheckerContext &C) const; +}; + +llvm::ImmutableListFactory Factory; + +/// 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. +/// +/// Note that this class is immutable, and new fields may only be added through +/// constructor calls. +class FieldChainInfo { + using FieldChain = llvm::ImmutableList; + + FieldChain Chain; + + const bool IsDereferenced = false; + +public: + FieldChainInfo() = default; + + FieldChainInfo(const FieldChainInfo &Other, const bool IsDereferenced) + : IsDereferenced(IsDereferenced) {} + + /// Delegates to the copy ctor and adds FR to Chain. + FieldChainInfo(const FieldChainInfo &Other, const FieldRegion *FR, + const bool IsDereferenced = false); + + bool contains(const FieldRegion *FR) const { return Chain.contains(FR); } + bool isPointer() const; + + /// If this is a fieldchain whose last element is an uninitialized region of a + /// pointer type, `IsDereferenced` will store whether the pointer itself or + /// the pointee is uninitialized. + bool isDereferenced() const; + const FieldDecl *getEndOfChain() const; + void print(llvm::raw_ostream &Out) const; + +private: + /// Prints every element except the last to `Out`. Since ImmutableLists store + /// elements in reverse order, and have no reverse iterators, we use a + /// recursive function to print the fieldchain correctly. The last element in + /// the chain is to be printed by `print`. + static void printTail(llvm::raw_ostream &Out, + const llvm::ImmutableListImpl *L); + friend struct FieldChainInfoComparator; +}; + +struct FieldChainInfoComparator { + bool operator()(const FieldChainInfo &lhs, const FieldChainInfo &rhs) { + assert(!lhs.Chain.isEmpty() && !rhs.Chain.isEmpty() && + "Attempted to store an empty fieldchain!"); + return *lhs.Chain.begin() < *rhs.Chain.begin(); + } +}; + +using UninitFieldSet = std::set; + +/// Searches for and stores uninitialized fields in a non-union object. +class FindUninitializedFields { + ProgramStateRef State; + const TypedValueRegion *const ObjectR; + + const bool IsPedantic; + bool IsAnyFieldInitialized = false; + + UninitFieldSet UninitFields; + +public: + FindUninitializedFields(ProgramStateRef State, + const TypedValueRegion *const R, bool IsPedantic); + const UninitFieldSet &getUninitFields(); + +private: + /// Adds a FieldChainInfo object to UninitFields. Return true if an insertion + /// took place. + bool 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 + // - an array + // - of a member pointer type + // - of a primitive type, which we'll define as either a BuiltinType or + // EnumeralType. + // * the parent of each node is the object that contains it + // * every leaf is an array, a primitive object, a member pointer, 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. An uninitialized field may be a + // primitive object, a member pointer, a pointer, a pointee or a union without + // a single initialized field. + // In the above example, for the default constructor call we'll end up with + // these fieldchains: + // + // this->b.x + // this->iptr (pointee uninit) + // this->bptr (pointer uninit) + // + // We'll traverse each node of the above graph with the appropiate one of + // these methods: + + /// This method checks a region of a union object, and returns true if no + /// field is initialized within the region. + bool isUnionUninit(const TypedValueRegion *R); + + /// This method checks a region of a non-union object, and returns true if + /// an uninitialized field is found within the region. + bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain); + + /// This method checks a region of a pointer or reference object, and returns + /// true if the ptr/ref object itself or any field within the pointee's region + /// is uninitialized. + bool isPointerOrReferenceUninit(const FieldRegion *FR, + FieldChainInfo LocalChain); + + /// This method checks a region of MemberPointerType, and returns true if the + /// the pointer is uninitialized. + bool isMemberPointerUninit(const FieldRegion *FR, FieldChainInfo LocalChain); + + /// This method returns true if the value of a primitive object is + /// uninitialized. + bool isPrimitiveUninit(const SVal &V); + + // Note that we don't have a method for arrays -- the elements of an array are + // often left uninitialized intentionally even when it is of a C++ record + // type, so we'll assume that an array is always initialized. + // TODO: Add a support for nonloc::LocAsInteger. +}; + +// Utility function declarations. + +/// Returns 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. +bool isCalledByConstructor(const CheckerContext &Context); + +/// Returns whether FD can be (transitively) dereferenced to a void pointer type +/// (void*, void**, ...). The type of the region behind a void pointer isn't +/// known, and thus FD can not be analyzed. +bool isVoidPointer(const FieldDecl *FD); + +/// Returns true if T is a primitive type. We'll call a type primitive if it's +/// either a BuiltinType or an EnumeralType. +bool isPrimitiveType(const QualType &T) { + return T->isBuiltinType() || T->isEnumeralType(); +} + +} // end of anonymous namespace + +//===----------------------------------------------------------------------===// +// Methods for UninitializedObjectChecker. +//===----------------------------------------------------------------------===// + +void UninitializedObjectChecker::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; + + // This avoids essentially the same error being reported multiple times. + if (isCalledByConstructor(Context)) + return; + + Optional Object = getObjectVal(CtorDecl, Context); + if (!Object) + return; + + FindUninitializedFields F(Context.getState(), Object->getRegion(), + IsPedantic); + + const UninitFieldSet &UninitFields = F.getUninitFields(); + + if (UninitFields.empty()) + return; + + // There are uninitialized fields in the record. + + 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()); + + SmallString<100> WarningBuf; + llvm::raw_svector_ostream WarningOS(WarningBuf); + WarningOS << UninitFields.size() << " uninitialized field" + << (UninitFields.size() == 1 ? "" : "s") + << " at the end of the constructor call"; + + auto Report = llvm::make_unique( + *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing, + Node->getLocationContext()->getDecl()); + + // TODO: As of now, one warning is emitted per constructor call, and the + // uninitialized fields are listed in notes. Until there's a better support + // for notes avaible, a note-less version of this checker should be + // implemented. + for (const auto &FieldChain : UninitFields) { + 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->"; + FieldChain.print(NoteOS); + NoteOS << "'"; + + Report->addNote(NoteOS.str(), + PathDiagnosticLocation::create(FieldChain.getEndOfChain(), + Context.getSourceManager())); + } + + Context.emitReport(std::move(Report)); +} + +//===----------------------------------------------------------------------===// +// Methods for FindUninitializedFields. +//===----------------------------------------------------------------------===// + +FindUninitializedFields::FindUninitializedFields( + ProgramStateRef State, const TypedValueRegion *const R, bool IsPedantic) + : State(State), ObjectR(R), IsPedantic(IsPedantic) {} + +const UninitFieldSet &FindUninitializedFields::getUninitFields() { + isNonUnionUninit(ObjectR, FieldChainInfo()); + + if (!IsPedantic && !IsAnyFieldInitialized) + UninitFields.clear(); + + return UninitFields; +} + +bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain) { + if (State->getStateManager().getContext().getSourceManager().isInSystemHeader( + Chain.getEndOfChain()->getLocation())) + return false; + + return UninitFields.insert(Chain).second; +} + +bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R, + FieldChainInfo LocalChain) { + assert(R->getValueType()->isRecordType() && + !R->getValueType()->isUnionType() && + "This method only checks non-union record objects!"); + + 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 auto FieldVal = + State->getLValue(I, loc::MemRegionVal(R)).castAs(); + const auto *FR = FieldVal.getRegionAs(); + QualType T = I->getType(); + + // If LocalChain already contains FR, then we encountered a cyclic + // reference. In this case, region FR is already under checking at an + // earlier node in the directed tree. + if (LocalChain.contains(FR)) + return false; + + if (T->isStructureOrClassType()) { + if (isNonUnionUninit(FR, {LocalChain, FR})) + ContainsUninitField = true; + continue; + } + + if (T->isUnionType()) { + if (isUnionUninit(FR)) { + if (addFieldToUninits({LocalChain, FR})) + ContainsUninitField = true; + } else + IsAnyFieldInitialized = true; + continue; + } + + if (T->isArrayType()) { + IsAnyFieldInitialized = true; + continue; + } + + if (T->isMemberPointerType()) { + if (isMemberPointerUninit(FR, LocalChain)) + ContainsUninitField = true; + continue; + } + + // If this is a pointer or reference type. + if (T->isPointerType() || T->isReferenceType()) { + if (isPointerOrReferenceUninit(FR, LocalChain)) + ContainsUninitField = true; + continue; + } + + assert(isPrimitiveType(T) && "Non-primitive type! " + "At this point FR must be primitive!"); + + SVal V = State->getSVal(FieldVal); + + if (isPrimitiveUninit(V)) { + if (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 *BaseRegion = State->getLValue(BaseSpec, R) + .castAs() + .getRegionAs(); + + if (isNonUnionUninit(BaseRegion, LocalChain)) + ContainsUninitField = true; + } + + return ContainsUninitField; +} + +bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) { + assert(R->getValueType()->isUnionType() && + "This method only checks union objects!"); + // 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) { + + assert((FR->getDecl()->getType()->isPointerType() || + FR->getDecl()->getType()->isReferenceType()) && + "This method only checks pointer/reference objects!"); + + SVal V = State->getSVal(FR); + + if (V.isUnknown() || V.isZeroConstant()) { + IsAnyFieldInitialized = true; + return false; + } + + if (V.isUndef()) { + return addFieldToUninits({LocalChain, FR}); + } + + const FieldDecl *FD = FR->getDecl(); + + // TODO: The dynamic type of a void pointer may be retrieved with + // `getDynamicTypeInfo`. + if (isVoidPointer(FD)) { + IsAnyFieldInitialized = true; + return false; + } + + assert(V.getAs() && "V should be Loc at this point!"); + + // At this point the pointer itself is initialized and points to a valid + // location, we'll now check the pointee. + SVal DerefdV = State->getSVal(V.castAs()); + + // TODO: Dereferencing should be done according to the dynamic type. + while (Optional L = DerefdV.getAs()) { + DerefdV = State->getSVal(*L); + } + + // If V is a pointer pointing to a record type. + if (Optional RecordV = + DerefdV.getAs()) { + + const TypedValueRegion *R = 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 Static Analyzer will construct a symbolic region for Node *next; or + // similar code snippets. + if (R->getSymbolicBase()) { + IsAnyFieldInitialized = true; + return false; + } + + const QualType T = R->getValueType(); + + if (T->isStructureOrClassType()) + return isNonUnionUninit(R, {LocalChain, FR}); + + if (T->isUnionType()) { + if (isUnionUninit(R)) { + return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true}); + } else { + IsAnyFieldInitialized = true; + return false; + } + } + + if (T->isArrayType()) { + IsAnyFieldInitialized = true; + return false; + } + + llvm_unreachable("All cases are handled!"); + } + + // TODO: If possible, it should be asserted that the DerefdV at this point is + // primitive. + + if (isPrimitiveUninit(DerefdV)) + return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true}); + + IsAnyFieldInitialized = true; + return false; +} + +bool FindUninitializedFields::isMemberPointerUninit(const FieldRegion *FR, + FieldChainInfo LocalChain) { + assert(FR->getDecl()->getType()->isMemberPointerType() && + "This function only checks regions that hold MemberPointerTypes!"); + // TODO: Implement support for MemberPointerTypes. + return false; +} + +bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) { + if (V.isUndef()) + return true; + + IsAnyFieldInitialized = true; + return false; +} + +//===----------------------------------------------------------------------===// +// Methods for FieldChainInfo. +//===----------------------------------------------------------------------===// + +FieldChainInfo::FieldChainInfo(const FieldChainInfo &Other, + const FieldRegion *FR, const bool IsDereferenced) + : FieldChainInfo(Other, IsDereferenced) { + assert(!contains(FR) && "Can't add a field that is already a part of the " + "fieldchain! Is this a cyclic reference?"); + Chain = Factory.add(FR, Other.Chain); +} + +bool FieldChainInfo::isPointer() const { + assert(!Chain.isEmpty() && "Empty fieldchain!"); + return (*Chain.begin())->getDecl()->getType()->isPointerType(); +} + +bool FieldChainInfo::isDereferenced() const { + assert(isPointer() && "Only pointers may or may not be dereferenced!"); + return IsDereferenced; +} + +const FieldDecl *FieldChainInfo::getEndOfChain() const { + assert(!Chain.isEmpty() && "Empty fieldchain!"); + return (*Chain.begin())->getDecl(); +} + +// TODO: This function constructs an incorrect fieldchain string in the +// following case: +// +// struct Base { int x; }; +// struct D1 : Base {}; struct D2 : Base {}; +// +// struct MostDerived : D1, D2 { +// MostDerived() {} +// } +// +// A call to MostDerived::MostDerived() will cause two notes that say +// "uninitialized field 'this->x'", but we can't refer to 'x' directly, +// we need an explicit namespace resolution whether the uninit field was +// 'D1::x' or 'D2::x'. +// +// TODO: If a field in the fieldchain is a captured lambda parameter, this +// function constructs an empty string for it: +// +// template struct A { +// Callable c; +// A(const Callable &c, int) : c(c) {} +// }; +// +// int b; // say that this isn't zero initialized +// auto alwaysTrue = [&b](int a) { return true; }; +// +// A call with these parameters: A::A(alwaysTrue, int()) +// will emit a note with the message "uninitialized field: 'this->c.'". If +// possible, the lambda parameter name should be retrieved or be replaced with a +// "" or something similar. +void FieldChainInfo::print(llvm::raw_ostream &Out) const { + if (Chain.isEmpty()) + return; + + const llvm::ImmutableListImpl *L = + Chain.getInternalPointer(); + printTail(Out, L->getTail()); + Out << L->getHead()->getDecl()->getNameAsString(); +} + +void FieldChainInfo::printTail( + llvm::raw_ostream &Out, + const llvm::ImmutableListImpl *L) { + if (!L) + return; + + printTail(Out, L->getTail()); + const FieldDecl *Field = L->getHead()->getDecl(); + Out << Field->getNameAsString(); + Out << (Field->getType()->isPointerType() ? "->" : "."); +} + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +namespace { + +bool isVoidPointer(const FieldDecl *FD) { + QualType T = FD->getType(); + + while (!T.isNull()) { + if (T->isVoidPointerType()) + return true; + T = T->getPointeeType(); + } + return false; +} + +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(); +} + +// TODO: We should also check that if the constructor was called by another +// constructor, whether those two are in any relation to one another. In it's +// current state, this introduces some false negatives. +bool isCalledByConstructor(const CheckerContext &Context) { + const LocationContext *LC = Context.getLocationContext()->getParent(); + + while (LC) { + if (isa(LC->getDecl())) + return true; + + LC = LC->getParent(); + } + return false; +} + +} // end of anonymous namespace + +void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) { + auto Chk = Mgr.registerChecker(); + Chk->IsPedantic = Mgr.getAnalyzerOptions().getBooleanOption( + "Pedantic", /*DefaultVal*/ false, Chk); +} Index: test/Analysis/Inputs/system-header-simulator-for-cxx-uninitialized-object.h =================================================================== --- /dev/null +++ test/Analysis/Inputs/system-header-simulator-for-cxx-uninitialized-object.h @@ -0,0 +1,18 @@ +// Like the compiler, the static analyzer treats some functions differently if +// they come from a system header -- for example, it is assumed that system +// functions do not arbitrarily free() their parameters, and that some bugs +// found in system headers cannot be fixed by the user and should be +// suppressed. + +#pragma clang system_header + +struct RecordInSystemHeader { + int a; + int b; +}; + +template +struct ContainerInSystemHeader { + T &t; + ContainerInSystemHeader(T& t) : t(t) {} +}; Index: test/Analysis/cxx-uninitialized-object-inheritance.cpp =================================================================== --- /dev/null +++ test/Analysis/cxx-uninitialized-object-inheritance.cpp @@ -0,0 +1,775 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.UninitializedObject -analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true -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 fNonPolymorphicInheritanceTest1() { + NonPolymorphicInheritanceTest1(); +} + +class NonPolymorphicBaseClass2 { + int x; // expected-note{{uninitialized field 'this->x'}} +protected: + int y; + +public: + NonPolymorphicBaseClass2() = default; + NonPolymorphicBaseClass2(int) : x(4) {} +}; + +class NonPolymorphicInheritanceTest2 : public NonPolymorphicBaseClass2 { + int z; + +public: + NonPolymorphicInheritanceTest2() { + y = 5; + z = 6; // expected-warning{{1 uninitialized field}} + } +}; + +void fNonPolymorphicInheritanceTest2() { + 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 fNonPolymorphicInheritanceTest3() { + 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 fNonPolymorphicInheritanceTest4() { + 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 fPolymorphicInheritanceTest1() { + 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 fPolymorphicInheritanceTest2() { + 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 fPolymorphicInheritanceTest3() { + 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 fPolymorphicInheritanceTest4() { + 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 fVirtualInheritanceTest1() { + 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 fVirtualInheritanceTest2() { + 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 fVirtualInheritanceTest3() { + 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 fMultipleInheritanceTest1() { + 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 fMultipleInheritanceTest2() { + 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 fMultipleInheritanceTest3() { + 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 fMultipleInheritanceTest4() { + 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 fMultipleInheritanceTest5() { + 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 fNonVirtualDiamondInheritanceTest1() { + 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 fNonVirtualDiamondInheritanceTest2() { + 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 fNonVirtualDiamondInheritanceTest3() { + 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 fNonVirtualDiamondInheritanceTest4() { + 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 fNonVirtualDiamondInheritanceTest5() { + 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 fNonVirtualDiamondInheritanceTest6() { + NonVirtualDiamondInheritanceTest6(); +} + +//===----------------------------------------------------------------------===// +// Virtual diamond inheritance tests +//===----------------------------------------------------------------------===// + +/* + VirtualBase + / \ + / \ + / \ + VirtualFirst VirtualSecond + \ / + \ / + \ / + VirtualDiamondInheritanceTest +*/ + +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 fVirtualDiamondInheritanceTest1() { + 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 fVirtualDiamondInheritanceTest2() { + 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 fVirtualDiamondInheritanceTest3() { + VirtualDiamondInheritanceTest3(); +} Index: test/Analysis/cxx-uninitialized-object-ptr-ref.cpp =================================================================== --- /dev/null +++ test/Analysis/cxx-uninitialized-object-ptr-ref.cpp @@ -0,0 +1,699 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.UninitializedObject -analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true -std=c++11 -DPEDANTIC -verify %s + +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.UninitializedObject -std=c++11 -verify %s + +//===----------------------------------------------------------------------===// +// Concrete location tests. +//===----------------------------------------------------------------------===// + +struct ConcreteIntLocTest { + int *ptr; + + ConcreteIntLocTest() : ptr(reinterpret_cast(0xDEADBEEF)) {} +}; + +void fConcreteIntLocTest() { + ConcreteIntLocTest(); +} + +//===----------------------------------------------------------------------===// +// Null pointer tests. +//===----------------------------------------------------------------------===// + +class NullPtrTest { + struct RecordType { + int x; + int y; + }; + + float *fptr = nullptr; + int *ptr; + RecordType *recPtr; + +public: + NullPtrTest() : ptr(nullptr), recPtr(nullptr) { + // All good! + } +}; + +void fNullPtrTest() { + NullPtrTest(); +} + +//===----------------------------------------------------------------------===// +// Heap pointer tests. +//===----------------------------------------------------------------------===// + +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 fHeapPointerTest1() { + 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 fHeapPointerTest2() { + HeapPointerTest2(); +} + +//===----------------------------------------------------------------------===// +// Stack pointer tests. +//===----------------------------------------------------------------------===// + +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 fStackPointerTest1() { + int ok_a = 28; + StackPointerTest1::RecordType ok_rec{29, 30}; + StackPointerTest1(&ok_a, &ok_rec); // 'a', 'rec.x', 'rec.y' uninitialized +} + +#ifdef PEDANTIC +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 fStackPointerTest2() { + int a; + StackPointerTest2::RecordType rec; + StackPointerTest2(&a, &rec); // 'a', 'rec.x', 'rec.y' uninitialized +} +#else +class StackPointerTest2 { +public: + struct RecordType { + int x; + int y; + }; + +private: + int *ptr; + RecordType *recPtr; + +public: + StackPointerTest2(int *_ptr, RecordType *_recPtr) : ptr(_ptr), recPtr(_recPtr) { + } +}; + +void fStackPointerTest2() { + int a; + StackPointerTest2::RecordType rec; + StackPointerTest2(&a, &rec); // 'a', 'rec.x', 'rec.y' uninitialized +} +#endif // PEDANTIC + +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 fUninitPointerTest() { + UninitPointerTest(); +} + +struct CharPointerTest { + const char *str; + int dontGetFilteredByNonPedanticMode = 0; + + CharPointerTest() : str("") {} +}; + +void fCharPointerTest() { + CharPointerTest(); +} + +struct CyclicPointerTest { + int *ptr; + CyclicPointerTest() : ptr(reinterpret_cast(&ptr)) {} +}; + +void fCyclicPointerTest() { + CyclicPointerTest(); +} + +//===----------------------------------------------------------------------===// +// Void pointer tests. +//===----------------------------------------------------------------------===// + +// Void pointer tests are mainly no-crash tests. + +void *malloc(int size); + +class VoidPointerTest1 { + void *vptr; + +public: + VoidPointerTest1(void *vptr, char) : vptr(vptr) { + // All good! + } +}; + +void fVoidPointerTest1() { + void *vptr = malloc(sizeof(int)); + VoidPointerTest1(vptr, char()); +} + +class VoidPointerTest2 { + void **vpptr; + +public: + VoidPointerTest2(void **vpptr, char) : vpptr(vpptr) { + // All good! + } +}; + +void fVoidPointerTest2() { + void *vptr = malloc(sizeof(int)); + VoidPointerTest2(&vptr, char()); +} + +class VoidPointerRRefTest1 { + void *&&vptrrref; + +public: + VoidPointerRRefTest1(void *vptr, char) : vptrrref(static_cast(vptr)) { + // All good! + } +}; + +void fVoidPointerRRefTest1() { + void *vptr = malloc(sizeof(int)); + VoidPointerRRefTest1(vptr, char()); +} + +class VoidPointerRRefTest2 { + void **&&vpptrrref; + +public: + VoidPointerRRefTest2(void **vptr, char) : vpptrrref(static_cast(vptr)) { + // All good! + } +}; + +void fVoidPointerRRefTest2() { + void *vptr = malloc(sizeof(int)); + VoidPointerRRefTest2(&vptr, char()); +} + +class VoidPointerLRefTest { + void *&vptrrref; + +public: + VoidPointerLRefTest(void *vptr, char) : vptrrref(static_cast(vptr)) { + // All good! + } +}; + +void fVoidPointerLRefTest() { + void *vptr = malloc(sizeof(int)); + VoidPointerLRefTest(vptr, char()); +} + +struct CyclicVoidPointerTest { + void *vptr; // no-crash + + CyclicVoidPointerTest() : vptr(&vptr) {} + +}; + +void fCyclicVoidPointerTest() { + CyclicVoidPointerTest(); +} + +//===----------------------------------------------------------------------===// +// Multipointer tests. +//===----------------------------------------------------------------------===// + +#ifdef PEDANTIC +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 fMultiPointerTest1() { + MultiPointerTest1::RecordType *p1; + MultiPointerTest1::RecordType **mptr = &p1; + MultiPointerTest1(mptr, int()); // '*mptr' uninitialized +} +#else +class MultiPointerTest1 { +public: + struct RecordType { + int x; + int y; + }; + +private: + RecordType **mptr; + +public: + MultiPointerTest1(RecordType **p, int) : mptr(p) {} +}; + +void fMultiPointerTest1() { + MultiPointerTest1::RecordType *p1; + MultiPointerTest1::RecordType **mptr = &p1; + MultiPointerTest1(mptr, int()); // '*mptr' uninitialized +} +#endif // PEDANTIC + +#ifdef PEDANTIC +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 fMultiPointerTest2() { + MultiPointerTest2::RecordType i; + MultiPointerTest2::RecordType *p1 = &i; + MultiPointerTest2::RecordType **mptr = &p1; + MultiPointerTest2(mptr, int()); // '**mptr' uninitialized +} +#else +class MultiPointerTest2 { +public: + struct RecordType { + int x; + int y; + }; + +private: + RecordType **mptr; + +public: + MultiPointerTest2(RecordType **p, int) : mptr(p) { + } +}; + +void fMultiPointerTest2() { + MultiPointerTest2::RecordType i; + MultiPointerTest2::RecordType *p1 = &i; + MultiPointerTest2::RecordType **mptr = &p1; + MultiPointerTest2(mptr, int()); // '**mptr' uninitialized +} +#endif // PEDANTIC + +class MultiPointerTest3 { +public: + struct RecordType { + int x; + int y; + }; + +private: + RecordType **mptr; + +public: + MultiPointerTest3(RecordType **p, int) : mptr(p) { + // All good! + } +}; + +void fMultiPointerTest3() { + MultiPointerTest3::RecordType i{31, 32}; + MultiPointerTest3::RecordType *p1 = &i; + MultiPointerTest3::RecordType **mptr = &p1; + MultiPointerTest3(mptr, int()); // '**mptr' uninitialized +} + +//===----------------------------------------------------------------------===// +// Member pointer tests. +//===----------------------------------------------------------------------===// + +struct UsefulFunctions { + int a, b; + + void print() {} + void dump() {} +}; + +#ifdef PEDANTIC +struct PointerToMemberFunctionTest1 { + // TODO: we'd expect the note {{uninitialized field 'this->f'}} + void (UsefulFunctions::*f)(void); // no-note + PointerToMemberFunctionTest1() {} +}; + +void fPointerToMemberFunctionTest1() { + // TODO: we'd expect the warning {{1 uninitialized field}} + PointerToMemberFunctionTest1(); // no-warning +} + +struct PointerToMemberFunctionTest2 { + void (UsefulFunctions::*f)(void); + PointerToMemberFunctionTest2(void (UsefulFunctions::*f)(void)) : f(f) { + // All good! + } +}; + +void fPointerToMemberFunctionTest2() { + void (UsefulFunctions::*f)(void) = &UsefulFunctions::print; + PointerToMemberFunctionTest2 a(f); +} + +struct MultiPointerToMemberFunctionTest1 { + void (UsefulFunctions::**f)(void); // expected-note{{uninitialized pointer 'this->f'}} + MultiPointerToMemberFunctionTest1() {} +}; + +void fMultiPointerToMemberFunctionTest1() { + MultiPointerToMemberFunctionTest1(); // expected-warning{{1 uninitialized field}} +} + +struct MultiPointerToMemberFunctionTest2 { + void (UsefulFunctions::**f)(void); + MultiPointerToMemberFunctionTest2(void (UsefulFunctions::**f)(void)) : f(f) { + // All good! + } +}; + +void fMultiPointerToMemberFunctionTest2() { + void (UsefulFunctions::*f)(void) = &UsefulFunctions::print; + MultiPointerToMemberFunctionTest2 a(&f); +} + +struct PointerToMemberDataTest1 { + // TODO: we'd expect the note {{uninitialized field 'this->f'}} + int UsefulFunctions::*d; // no-note + PointerToMemberDataTest1() {} +}; + +void fPointerToMemberDataTest1() { + // TODO: we'd expect the warning {{1 uninitialized field}} + PointerToMemberDataTest1(); // no-warning +} + +struct PointerToMemberDataTest2 { + int UsefulFunctions::*d; + PointerToMemberDataTest2(int UsefulFunctions::*d) : d(d) { + // All good! + } +}; + +void fPointerToMemberDataTest2() { + int UsefulFunctions::*d = &UsefulFunctions::a; + PointerToMemberDataTest2 a(d); +} + +struct MultiPointerToMemberDataTest1 { + int UsefulFunctions::**d; // expected-note{{uninitialized pointer 'this->d'}} + MultiPointerToMemberDataTest1() {} +}; + +void fMultiPointerToMemberDataTest1() { + MultiPointerToMemberDataTest1(); // expected-warning{{1 uninitialized field}} +} + +struct MultiPointerToMemberDataTest2 { + int UsefulFunctions::**d; + MultiPointerToMemberDataTest2(int UsefulFunctions::**d) : d(d) { + // All good! + } +}; + +void fMultiPointerToMemberDataTest2() { + int UsefulFunctions::*d = &UsefulFunctions::a; + MultiPointerToMemberDataTest2 a(&d); +} +#endif // PEDANTIC + +//===----------------------------------------------------------------------===// +// Tests for list-like records. +//===----------------------------------------------------------------------===// + +class ListTest1 { +public: + struct Node { + Node *next = nullptr; // no crash + int i; + }; + +private: + Node *head = nullptr; + +public: + ListTest1() { + // All good! + } +}; + +void fListTest1() { + 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 fListTest2() { + ListTest2::Node n; + ListTest2(&n, int()); +} + +class CyclicList { +public: + struct Node { + Node *next = nullptr; + int i; // expected-note{{uninitialized field 'this->head->i'}} + }; + +private: + Node *head = nullptr; + +public: + CyclicList(Node *node, int) : head(node) { // expected-warning{{1 uninitialized field}} + } +}; + +void fCyclicList() { + /* + n3 + / \ + this -- n1 -- n2 + */ + + CyclicList::Node n1; + CyclicList::Node n2; + n2.next = &n1; + n2.i = 50; + CyclicList::Node n3; + n3.next = &n2; + n3.i = 50; + n1.next = &n3; + // note that n1.i is uninitialized + CyclicList(&n1, 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 fReferenceTest1() { + ReferenceTest1::RecordType d{33, 34}; + ReferenceTest1(d, d); +} + +#ifdef PEDANTIC +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 fReferenceTest2() { + ReferenceTest2::RecordType c; + ReferenceTest2(c, c); +} +#else +class ReferenceTest2 { +public: + struct RecordType { + int x; + int y; + }; + +private: + RecordType &lref; + RecordType &&rref; + +public: + ReferenceTest2(RecordType &lref, RecordType &rref) + : lref(lref), rref(static_cast(rref)) { + } +}; + +void fReferenceTest2() { + ReferenceTest2::RecordType c; + ReferenceTest2(c, c); +} +#endif // PEDANTIC + +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 fReferenceTest3() { + 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 fReferenceTest5() { + ReferenceTest4::RecordType c, d{37, 38}; + ReferenceTest4(d, c); +} Index: test/Analysis/cxx-uninitialized-object.cpp =================================================================== --- /dev/null +++ test/Analysis/cxx-uninitialized-object.cpp @@ -0,0 +1,1058 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.UninitializedObject -analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true -std=c++11 -DPEDANTIC -verify %s + +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.UninitializedObject -std=c++11 -verify %s + +//===----------------------------------------------------------------------===// +// Default constructor test. +//===----------------------------------------------------------------------===// + +class CompilerGeneratedConstructorTest { + int a, b, c, d, e, f, g, h, i, j; + +public: + CompilerGeneratedConstructorTest() = default; +}; + +void fCompilerGeneratedConstructorTest() { + CompilerGeneratedConstructorTest(); +} + +#ifdef PEDANTIC +class DefaultConstructorTest { + int a; // expected-note{{uninitialized field 'this->a'}} + +public: + DefaultConstructorTest(); +}; + +DefaultConstructorTest::DefaultConstructorTest() = default; + +void fDefaultConstructorTest() { + DefaultConstructorTest(); // expected-warning{{1 uninitialized field}} +} +#else +class DefaultConstructorTest { + int a; + +public: + DefaultConstructorTest(); +}; + +DefaultConstructorTest::DefaultConstructorTest() = default; + +void fDefaultConstructorTest() { + DefaultConstructorTest(); +} +#endif // PEDANTIC + +//===----------------------------------------------------------------------===// +// Initializer list test. +//===----------------------------------------------------------------------===// + +class InitListTest1 { + int a; + int b; + +public: + InitListTest1() + : a(1), + b(2) { + // All good! + } +}; + +void fInitListTest1() { + InitListTest1(); +} + +class InitListTest2 { + int a; + int b; // expected-note{{uninitialized field 'this->b'}} + +public: + InitListTest2() + : a(3) {} // expected-warning{{1 uninitialized field}} +}; + +void fInitListTest2() { + InitListTest2(); +} + +class InitListTest3 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; + +public: + InitListTest3() + : b(4) {} // expected-warning{{1 uninitialized field}} +}; + +void fInitListTest3() { + InitListTest3(); +} + +//===----------------------------------------------------------------------===// +// Constructor body test. +//===----------------------------------------------------------------------===// + +class CtorBodyTest1 { + int a, b; + +public: + CtorBodyTest1() { + a = 5; + b = 6; + // All good! + } +}; + +void fCtorBodyTest1() { + CtorBodyTest1(); +} + +class CtorBodyTest2 { + int a; + int b; // expected-note{{uninitialized field 'this->b'}} + +public: + CtorBodyTest2() { + a = 7; // expected-warning{{1 uninitialized field}} + } +}; + +void fCtorBodyTest2() { + CtorBodyTest2(); +} + +class CtorBodyTest3 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; + +public: + CtorBodyTest3() { + b = 8; // expected-warning{{1 uninitialized field}} + } +}; + +void fCtorBodyTest3() { + CtorBodyTest3(); +} + +#ifdef PEDANTIC +class CtorBodyTest4 { + int a; // expected-note{{uninitialized field 'this->a'}} + int b; // expected-note{{uninitialized field 'this->b'}} + +public: + CtorBodyTest4() {} +}; + +void fCtorBodyTest4() { + CtorBodyTest4(); // expected-warning{{2 uninitialized fields}} +} +#else +class CtorBodyTest4 { + int a; + int b; + +public: + CtorBodyTest4() {} +}; + +void fCtorBodyTest4() { + CtorBodyTest4(); +} +#endif + +//===----------------------------------------------------------------------===// +// 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 fCtorDelegationTest1() { + 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 fCtorDelegationTest2() { + 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 fContainsRecordTest1() { + 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 fContainsRecordTest2() { + 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 fContainsRecordTest3() { + 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 fContainsRecordTest4() { + ContainsRecordTest4(); +} + +//===----------------------------------------------------------------------===// +// Tests for template classes. +//===----------------------------------------------------------------------===// + +template +class IntTemplateClassTest1 { + T t; + int b; + +public: + IntTemplateClassTest1(T i) { + b = 21; + t = i; + // All good! + } +}; + +void fIntTemplateClassTest1() { + 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 fIntTemplateClassTest2() { + 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 fRecordTemplateClassTest() { + RecordTemplateClassTest(); +} + +//===----------------------------------------------------------------------===// +// Tests involving functions with unknown implementations. +//===----------------------------------------------------------------------===// + +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 fPassingToUnknownFunctionTest1() { + 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 fPassingToUnknownFunctionTest2() { + PassingToUnknownFunctionTest2(); +} + +//===----------------------------------------------------------------------===// +// Tests for classes containing unions. +//===----------------------------------------------------------------------===// + +// FIXME: As of writing this checker, there is no good support for union types +// in the Static Analyzer. 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 fContainsSimpleUnionTest1() { + 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 fContainsSimpleUnionTest2() { + // 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 fUnionPointerTest1() { + 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 fUnionPointerTest2() { + 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 fContainsUnionWithRecordTest1() { + 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 fContainsUnionWithRecordTest2() { + 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 fContainsUnionWithRecordTest3() { + 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 fContainsUnionWithSimpleUnionTest1() { + 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 fContainsUnionWithSimpleUnionTest2() { + // TODO: we'd expect the warning: {{1 uninitialized field}} + ContainsUnionWithSimpleUnionTest2(); // no-warning +} + +//===----------------------------------------------------------------------===// +// Zero initialization tests. +//===----------------------------------------------------------------------===// + +struct GlobalVariableTest { + int i; + + GlobalVariableTest() {} +}; + +GlobalVariableTest gvt; // no-warning + +//===----------------------------------------------------------------------===// +// Copy and move constructor tests. +//===----------------------------------------------------------------------===// + +template +void funcToSquelchCompilerWarnings(const T &t); + +#ifdef PEDANTIC +struct CopyConstructorTest { + int i; // expected-note{{uninitialized field 'this->i'}} + + CopyConstructorTest() : i(1337) {} + CopyConstructorTest(const CopyConstructorTest &other) {} +}; + +void fCopyConstructorTest() { + CopyConstructorTest cct; + CopyConstructorTest copy = cct; // expected-warning{{1 uninitialized field}} + funcToSquelchCompilerWarnings(copy); +} +#else +struct CopyConstructorTest { + int i; + + CopyConstructorTest() : i(1337) {} + CopyConstructorTest(const CopyConstructorTest &other) {} +}; + +void fCopyConstructorTest() { + CopyConstructorTest cct; + CopyConstructorTest copy = cct; + funcToSquelchCompilerWarnings(copy); +} +#endif // PEDANTIC + +struct MoveConstructorTest { + // TODO: we'd expect the note: {{uninitialized field 'this->i'}} + int i; // no-note + + MoveConstructorTest() : i(1337) {} + MoveConstructorTest(const CopyConstructorTest &other) = delete; + MoveConstructorTest(const CopyConstructorTest &&other) {} +}; + +void fMoveConstructorTest() { + MoveConstructorTest cct; + // TODO: we'd expect the warning: {{1 uninitialized field}} + MoveConstructorTest copy(static_cast(cct)); // no-warning + funcToSquelchCompilerWarnings(copy); +} + +//===----------------------------------------------------------------------===// +// Array tests. +//===----------------------------------------------------------------------===// + +struct IntArrayTest { + int arr[256]; + + IntArrayTest() { + // All good! + } +}; + +void fIntArrayTest() { + IntArrayTest(); +} + +struct RecordTypeArrayTest { + struct RecordType { + int x, y; + } arr[256]; + + RecordTypeArrayTest() { + // All good! + } +}; + +void fRecordTypeArrayTest() { + RecordTypeArrayTest(); +} + +template +class CharArrayPointerTest { + T *t; // no-crash + +public: + CharArrayPointerTest(T *t, int) : t(t) {} +}; + +void fCharArrayPointerTest() { + char str[16] = "012345678912345"; + CharArrayPointerTest(&str, int()); +} + +//===----------------------------------------------------------------------===// +// Memset tests. +//===----------------------------------------------------------------------===// + +struct MemsetTest1 { + int a, b, c; + + MemsetTest1() { + __builtin_memset(this, 0, sizeof(decltype(*this))); + } +}; + +void fMemsetTest1() { + MemsetTest1(); +} + +struct MemsetTest2 { + int a; + + MemsetTest2() { + __builtin_memset(&a, 0, sizeof(int)); + } +}; + +void fMemsetTest2() { + MemsetTest2(); +} + +//===----------------------------------------------------------------------===// +// Lambda tests. +//===----------------------------------------------------------------------===// + +template +struct LambdaTest1 { + Callable functor; + + LambdaTest1(const Callable &functor, int) : functor(functor) { + // All good! + } +}; + +void fLambdaTest1() { + auto isEven = [](int a) { return a % 2 == 0; }; + LambdaTest1(isEven, int()); +} + +#ifdef PEDANTIC +template +struct LambdaTest2 { + Callable functor; + + LambdaTest2(const Callable &functor, int) : functor(functor) {} // expected-warning{{1 uninitialized field}} +}; + +void fLambdaTest2() { + int b; + auto equals = [&b](int a) { return a == b; }; // expected-note{{uninitialized field 'this->functor.'}} + LambdaTest2(equals, int()); +} +#else +template +struct LambdaTest2 { + Callable functor; + + LambdaTest2(const Callable &functor, int) : functor(functor) {} +}; + +void fLambdaTest2() { + int b; + auto equals = [&b](int a) { return a == b; }; + LambdaTest2(equals, int()); +} +#endif //PEDANTIC + +#ifdef PEDANTIC +namespace LT3Detail { + +struct RecordType { + int x; // expected-note{{uninitialized field 'this->functor..x'}} + int y; // expected-note{{uninitialized field 'this->functor..y'}} +}; + +} // namespace LT3Detail +template +struct LambdaTest3 { + Callable functor; + + LambdaTest3(const Callable &functor, int) : functor(functor) {} // expected-warning{{2 uninitialized fields}} +}; + +void fLambdaTest3() { + LT3Detail::RecordType rec1; + auto equals = [&rec1](LT3Detail::RecordType rec2) { + return rec1.x == rec2.x; + }; + LambdaTest3(equals, int()); +} +#else +namespace LT3Detail { + +struct RecordType { + int x; + int y; +}; + +} // namespace LT3Detail +template +struct LambdaTest3 { + Callable functor; + + LambdaTest3(const Callable &functor, int) : functor(functor) {} +}; + +void fLambdaTest3() { + LT3Detail::RecordType rec1; + auto equals = [&rec1](LT3Detail::RecordType rec2) { + return rec1.x == rec2.x; + }; + LambdaTest3(equals, int()); +} +#endif //PEDANTIC + +//===----------------------------------------------------------------------===// +// System header tests. +//===----------------------------------------------------------------------===// + +#include "Inputs/system-header-simulator-for-cxx-uninitialized-object.h" + +struct SystemHeaderTest1 { + RecordInSystemHeader rec; // defined in the system header simulator + + SystemHeaderTest1() { + // All good! + } +}; + +void fSystemHeaderTest1() { + SystemHeaderTest1(); +} + +#ifdef PEDANTIC +struct SystemHeaderTest2 { + struct RecordType { + int x; // expected-note{{uninitialized field 'this->container.t.x}} + int y; // expected-note{{uninitialized field 'this->container.t.y}} + }; + ContainerInSystemHeader container; + + SystemHeaderTest2(RecordType &rec, int) : container(rec) {} // expected-warning{{2 uninitialized fields}} +}; + +void fSystemHeaderTest2() { + SystemHeaderTest2::RecordType rec; + SystemHeaderTest2(rec, int()); +} +#else +struct SystemHeaderTest2 { + struct RecordType { + int x; + int y; + }; + ContainerInSystemHeader container; + + SystemHeaderTest2(RecordType &rec, int) : container(rec) {} +}; + +void fSystemHeaderTest2() { + SystemHeaderTest2::RecordType rec; + SystemHeaderTest2(rec, int()); +} +#endif //PEDANTIC + +//===----------------------------------------------------------------------===// +// Incomplete type tests. +//===----------------------------------------------------------------------===// + +struct IncompleteTypeTest1 { + struct RecordType; + // no-crash + RecordType *recptr; // expected-note{{uninitialized pointer 'this->recptr}} + int dontGetFilteredByNonPedanticMode = 0; + + IncompleteTypeTest1() {} // expected-warning{{1 uninitialized field}} +}; + +void fIncompleteTypeTest1() { + IncompleteTypeTest1(); +} + +struct IncompleteTypeTest2 { + struct RecordType; + RecordType *recptr; // no-crash + int dontGetFilteredByNonPedanticMode = 0; + + RecordType *recordTypeFactory(); + + IncompleteTypeTest2() : recptr(recordTypeFactory()) {} +}; + +void fIncompleteTypeTest2() { + IncompleteTypeTest2(); +} + +struct IncompleteTypeTest3 { + struct RecordType; + RecordType &recref; // no-crash + int dontGetFilteredByNonPedanticMode = 0; + + RecordType &recordTypeFactory(); + + IncompleteTypeTest3() : recref(recordTypeFactory()) {} +}; + +void fIncompleteTypeTest3() { + IncompleteTypeTest3(); +} + +//===----------------------------------------------------------------------===// +// Builtin type or enumeration type related tests. +//===----------------------------------------------------------------------===// + +struct IntegralTypeTest { + int a; // expected-note{{uninitialized field 'this->a'}} + int dontGetFilteredByNonPedanticMode = 0; + + IntegralTypeTest() {} // expected-warning{{1 uninitialized field}} +}; + +void fIntegralTypeTest() { + IntegralTypeTest(); +} + +struct FloatingTypeTest { + float a; // expected-note{{uninitialized field 'this->a'}} + int dontGetFilteredByNonPedanticMode = 0; + + FloatingTypeTest() {} // expected-warning{{1 uninitialized field}} +}; + +void fFloatingTypeTest() { + FloatingTypeTest(); +} + +struct NullptrTypeTypeTest { + decltype(nullptr) a; // expected-note{{uninitialized field 'this->a'}} + int dontGetFilteredByNonPedanticMode = 0; + + NullptrTypeTypeTest() {} // expected-warning{{1 uninitialized field}} +}; + +void fNullptrTypeTypeTest() { + NullptrTypeTypeTest(); +} + +struct EnumTest { + enum Enum { + A, + B + } enum1; // expected-note{{uninitialized field 'this->enum1'}} + enum class Enum2 { + A, + B + } enum2; // expected-note{{uninitialized field 'this->enum2'}} + int dontGetFilteredByNonPedanticMode = 0; + + EnumTest() {} // expected-warning{{2 uninitialized fields}} +}; + +void fEnumTest() { + EnumTest(); +} + +//===----------------------------------------------------------------------===// +// Tests for constructor calls within another cunstructor, without the two +// records being in any relation. +//===----------------------------------------------------------------------===// + +void halt() __attribute__((__noreturn__)); +void assert(int b) { + if (!b) + halt(); +} + +// While a singleton would make more sense as a static variable, that would zero +// initialize all of its fields, hence the not too practical implementation. +struct Singleton { + // TODO: we'd expect the note: {{uninitialized field 'this->i'}} + int i; // no-note + + Singleton() { + assert(!isInstantiated); + // TODO: we'd expect the warning: {{1 uninitialized field}} + isInstantiated = true; // no-warning + } + + ~Singleton() { + isInstantiated = false; + } + + static bool isInstantiated; +}; + +bool Singleton::isInstantiated = false; + +struct SingletonTest { + int dontGetFilteredByNonPedanticMode = 0; + + SingletonTest() { + Singleton(); + } +}; + +void fSingletonTest() { + SingletonTest(); +} + +//===----------------------------------------------------------------------===// +// C++11 member initializer tests. +//===----------------------------------------------------------------------===// + +struct CXX11MemberInitTest1 { + int a = 3; + int b; + CXX11MemberInitTest1() : b(2) { + // All good! + } +}; + +void fCXX11MemberInitTest1() { + CXX11MemberInitTest1(); +} + +struct CXX11MemberInitTest2 { + struct RecordType { + // TODO: we'd expect the note: {{uninitialized field 'this->rec.a'}} + int a; // no-note + // TODO: we'd expect the note: {{uninitialized field 'this->rec.b'}} + int b; // no-note + + RecordType(int) {} + }; + + RecordType rec = RecordType(int()); + int dontGetFilteredByNonPedanticMode = 0; + + CXX11MemberInitTest2() {} +}; + +void fCXX11MemberInitTest2() { + // TODO: we'd expect the warning: {{2 uninitializeds field}} + CXX11MemberInitTest2(); // no-warning +}