[analyzer] Checker for uninitialized C++ objects

This checker analyzes C++ constructor calls, and reports uninitialized fields.

Due to the nature of this problem (uninitialized fields after an object
construction), this checker doesn't search for bugs, but rather is a tool to
enforce a specific programming model where every field needs to be initialized.

This checker lands in alpha for now, and a number of followup patches will be
made to reduce false negatives and to make it easier for the user to understand
what rules the checker relies on, eg. whether a derived class' constructor is
responsible for initializing inherited data members or whether it should be
handled in the base class' constructor.

Differential Revision: https://reviews.llvm.org/D45532

llvm-svn: 334935
This commit is contained in:
Kristof Umann 2018-06-18 11:50:17 +00:00
parent 99a5832016
commit 30f086565c
7 changed files with 3224 additions and 0 deletions

View File

@ -319,6 +319,10 @@ def MisusedMovedObjectChecker: Checker<"MisusedMovedObject">,
"object will be reported">,
DescFile<"MisusedMovedObjectChecker.cpp">;
def UninitializedObjectChecker: Checker<"UninitializedObject">,
HelpText<"Reports uninitialized fields after object construction">,
DescFile<"UninitializedObjectChecker.cpp">;
} // end: "alpha.cplusplus"

View File

@ -92,6 +92,7 @@ add_clang_library(clangStaticAnalyzerCheckers
UndefResultChecker.cpp
UndefinedArraySubscriptChecker.cpp
UndefinedAssignmentChecker.cpp
UninitializedObjectChecker.cpp
UnixAPIChecker.cpp
UnreachableCodeChecker.cpp
VforkChecker.cpp

View File

@ -0,0 +1,669 @@
//===----- 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 <algorithm>
using namespace clang;
using namespace clang::ento;
namespace {
class UninitializedObjectChecker : public Checker<check::EndFunction> {
std::unique_ptr<BuiltinBug> 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<const FieldRegion *> 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<const FieldRegion *>;
FieldChain Chain;
const bool IsDereferenced = false;
public:
FieldChainInfo() = default;
FieldChainInfo(const FieldChainInfo &Other, const bool IsDereferenced)
: Chain(Other.Chain), IsDereferenced(IsDereferenced) {}
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<const FieldRegion *> *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<FieldChainInfo, FieldChainInfoComparator>;
/// 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<nonloc::LazyCompoundVal>
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<CXXConstructorDecl>(
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<nonloc::LazyCompoundVal> 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<BugReport>(
*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<RecordType>()->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<loc::MemRegionVal>();
const auto *FR = FieldVal.getRegionAs<FieldRegion>();
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.
// FIXME: As of now, because of `isCalledByConstructor`, objects whose type
// is a descendant of another type will emit warnings for uninitalized
// inherited members.
// This is not the only way to analyze bases of an object -- if we didn't
// filter them out, and didn't analyze the bases, this checker would run for
// each base of the object in order of base initailization and in theory would
// find every uninitalized field. This approach could also make handling
// diamond inheritances more easily.
//
// This rule (that a descendant type's cunstructor is responsible for
// initializing inherited data members) is not obvious, and should it should
// be.
const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
if (!CXXRD)
return ContainsUninitField;
for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) {
const auto *BaseRegion = State->getLValue(BaseSpec, R)
.castAs<loc::MemRegionVal>()
.getRegionAs<TypedValueRegion>();
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<Loc>() && "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<Loc>());
// TODO: Dereferencing should be done according to the dynamic type.
while (Optional<Loc> L = DerefdV.getAs<Loc>()) {
DerefdV = State->getSVal(*L);
}
// If V is a pointer pointing to a record type.
if (Optional<nonloc::LazyCompoundVal> RecordV =
DerefdV.getAs<nonloc::LazyCompoundVal>()) {
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 <class Callable> 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<decltype(alwaysTrue)>::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
// "<lambda parameter>" or something similar.
void FieldChainInfo::print(llvm::raw_ostream &Out) const {
if (Chain.isEmpty())
return;
const llvm::ImmutableListImpl<const FieldRegion *> *L =
Chain.getInternalPointer();
printTail(Out, L->getTail());
Out << L->getHead()->getDecl()->getNameAsString();
}
void FieldChainInfo::printTail(
llvm::raw_ostream &Out,
const llvm::ImmutableListImpl<const FieldRegion *> *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<nonloc::LazyCompoundVal>
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<Loc>());
return Object.getAs<nonloc::LazyCompoundVal>();
}
// 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<CXXConstructorDecl>(LC->getDecl()))
return true;
LC = LC->getParent();
}
return false;
}
} // end of anonymous namespace
void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
Chk->IsPedantic = Mgr.getAnalyzerOptions().getBooleanOption(
"Pedantic", /*DefaultVal*/ false, Chk);
}

View File

@ -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 <class T>
struct ContainerInSystemHeader {
T &t;
ContainerInSystemHeader(T& t) : t(t) {}
};

View File

@ -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();
}

View File

@ -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<int *>(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<int*>(&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<void *&&>(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<void **&&>(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<void *&>(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<RecordType &&>(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<RecordType &&>(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<RecordType &&>(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<RecordType &&>(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<RecordType &&>(rref)) { // expected-warning{{2 uninitialized fields}}
}
};
void fReferenceTest5() {
ReferenceTest4::RecordType c, d{37, 38};
ReferenceTest4(d, c);
}

File diff suppressed because it is too large Load Diff