forked from OSchip/llvm-project
[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:
parent
99a5832016
commit
30f086565c
|
@ -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"
|
||||
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ add_clang_library(clangStaticAnalyzerCheckers
|
|||
UndefResultChecker.cpp
|
||||
UndefinedArraySubscriptChecker.cpp
|
||||
UndefinedAssignmentChecker.cpp
|
||||
UninitializedObjectChecker.cpp
|
||||
UnixAPIChecker.cpp
|
||||
UnreachableCodeChecker.cpp
|
||||
VforkChecker.cpp
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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) {}
|
||||
};
|
|
@ -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();
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue