r339595 - [analyzer][UninitializedObjectChecker] Refactoring p2.: Moving pointer chasing to a separate file
Kristof Umann via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 13 11:17:05 PDT 2018
Author: szelethus
Date: Mon Aug 13 11:17:05 2018
New Revision: 339595
URL: http://llvm.org/viewvc/llvm-project?rev=339595&view=rev
Log:
[analyzer][UninitializedObjectChecker] Refactoring p2.: Moving pointer chasing to a separate file
In this patch, the following classes and functions have been moved to a header file:
FieldChainInfo
FindUninitializedFields
isPrimitiveType
This also meant that they moved from anonymous namespace to clang::ento.
Code related to pointer chasing now relies in its own file.
There's absolutely no functional change in this patch -- its literally just copy pasting.
Differential Revision: https://reviews.llvm.org/D50504
Added:
cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/
cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
Removed:
cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
Modified:
cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt?rev=339595&r1=339594&r2=339595&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/CMakeLists.txt Mon Aug 13 11:17:05 2018
@@ -93,7 +93,8 @@ add_clang_library(clangStaticAnalyzerChe
UndefResultChecker.cpp
UndefinedArraySubscriptChecker.cpp
UndefinedAssignmentChecker.cpp
- UninitializedObjectChecker.cpp
+ UninitializedObject/UninitializedObjectChecker.cpp
+ UninitializedObject/UninitializedPointee.cpp
UnixAPIChecker.cpp
UnreachableCodeChecker.cpp
VforkChecker.cpp
Added: cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h?rev=339595&view=auto
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h (added)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h Mon Aug 13 11:17:05 2018
@@ -0,0 +1,196 @@
+//===----- UninitializedObject.h ---------------------------------*- 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 helper classes for UninitializedObjectChecker and
+// documentation about the logic of it.
+//
+// To read about command line options and a description what this checker does,
+// refer to UninitializedObjectChecker.cpp.
+//
+// Some methods are implemented in UninitializedPointee.cpp, to reduce the
+// complexity of the main checker file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H
+#define LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+namespace clang {
+namespace ento {
+
+/// 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 {
+public:
+ using FieldChain = llvm::ImmutableList<const FieldRegion *>;
+
+private:
+ FieldChain::Factory &Factory;
+ FieldChain Chain;
+
+ const bool IsDereferenced = false;
+
+public:
+ FieldChainInfo() = delete;
+ FieldChainInfo(FieldChain::Factory &F) : Factory(F) {}
+
+ FieldChainInfo(const FieldChainInfo &Other, const bool IsDereferenced)
+ : Factory(Other.Factory), 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) const {
+ 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;
+ const bool CheckPointeeInitialization;
+
+ bool IsAnyFieldInitialized = false;
+
+ FieldChainInfo::FieldChain::Factory Factory;
+ UninitFieldSet UninitFields;
+
+public:
+ FindUninitializedFields(ProgramStateRef State,
+ const TypedValueRegion *const R, bool IsPedantic,
+ bool CheckPointeeInitialization);
+ 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 primitive type, which we'll define later in a helper function.
+ // * the parent of each node is the object that contains it
+ // * every leaf is an array, a primitive object, a nullptr or an undefined
+ // pointer.
+ //
+ // Example:
+ //
+ // struct A {
+ // struct B {
+ // int x, y = 0;
+ // };
+ // B b;
+ // int *iptr = new int;
+ // B* bptr;
+ //
+ // A() {}
+ // };
+ //
+ // The directed tree:
+ //
+ // ->x
+ // /
+ // ->b--->y
+ // /
+ // A-->iptr->(int value)
+ // \
+ // ->bptr
+ //
+ // From this we'll construct a vector of fieldchains, where each fieldchain
+ // represents an uninitialized field. An uninitialized field may be a
+ // primitive object, 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 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.
+};
+
+/// Returns true if T is a primitive type. We defined this type so that for
+/// objects that we'd only like analyze as much as checking whether their
+/// value is undefined or not, such as ints and doubles, can be analyzed with
+/// ease. This also helps ensuring that every special field type is handled
+/// correctly.
+static bool isPrimitiveType(const QualType &T) {
+ return T->isBuiltinType() || T->isEnumeralType() || T->isMemberPointerType();
+}
+
+} // end of namespace ento
+} // end of namespace clang
+
+#endif // LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H
Added: cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp?rev=339595&view=auto
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp (added)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp Mon Aug 13 11:17:05 2018
@@ -0,0 +1,487 @@
+//===----- 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 several options:
+// - "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`.
+//
+// - "NotesAsWarnings" (boolean). If set to true, the checker will emit a
+// warning for each uninitalized field, as opposed to emitting one warning
+// per constructor call, and listing the uninitialized fields that belongs
+// to it in notes. Defaults to false.
+//
+// `-analyzer-config \
+// alpha.cplusplus.UninitializedObject:NotesAsWarnings=true`.
+//
+// - "CheckPointeeInitialization" (boolean). If set to false, the checker will
+// not analyze the pointee of pointer/reference fields, and will only check
+// whether the object itself is initialized. Defaults to false.
+//
+// `-analyzer-config \
+// alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=true`.
+//
+// TODO: With some clever heuristics, some pointers should be dereferenced
+// by default. For example, if the pointee is constructed within the
+// constructor call, it's reasonable to say that no external object
+// references it, and we wouldn't generate multiple report on the same
+// pointee.
+//
+// To read about how the checker works, refer to the comments in
+// UninitializedObject.h.
+//
+// Some of the logic is implemented in UninitializedPointee.cpp, to reduce the
+// complexity of this file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UninitializedObject.h"
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+
+using namespace clang;
+using namespace clang::ento;
+
+namespace {
+
+class UninitializedObjectChecker : public Checker<check::EndFunction> {
+ std::unique_ptr<BuiltinBug> BT_uninitField;
+
+public:
+ // These fields will be initialized when registering the checker.
+ bool IsPedantic;
+ bool ShouldConvertNotesToWarnings;
+ bool CheckPointeeInitialization;
+
+ UninitializedObjectChecker()
+ : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {}
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+};
+
+} // end of anonymous namespace
+
+// Utility function declarations.
+
+/// Returns the object that was constructed by CtorDecl, or None if that isn't
+/// possible.
+// TODO: Refactor this function so that it returns the constructed object's
+// region.
+static Optional<nonloc::LazyCompoundVal>
+getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context);
+
+/// Checks whether the object constructed by \p Ctor will be analyzed later
+/// (e.g. if the object is a field of another object, in which case we'd check
+/// it multiple times).
+static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
+ CheckerContext &Context);
+
+/// Constructs a note message for a given FieldChainInfo object.
+static void printNoteMessage(llvm::raw_ostream &Out,
+ const FieldChainInfo &Chain);
+
+/// Returns with Field's name. This is a helper function to get the correct name
+/// even if Field is a captured lambda variable.
+static StringRef getVariableName(const FieldDecl *Field);
+
+//===----------------------------------------------------------------------===//
+// Methods for UninitializedObjectChecker.
+//===----------------------------------------------------------------------===//
+
+void UninitializedObjectChecker::checkEndFunction(
+ const ReturnStmt *RS, 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 (willObjectBeAnalyzedLater(CtorDecl, Context))
+ return;
+
+ Optional<nonloc::LazyCompoundVal> Object = getObjectVal(CtorDecl, Context);
+ if (!Object)
+ return;
+
+ FindUninitializedFields F(Context.getState(), Object->getRegion(), IsPedantic,
+ CheckPointeeInitialization);
+
+ 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());
+
+ // For Plist consumers that don't support notes just yet, we'll convert notes
+ // to warnings.
+ if (ShouldConvertNotesToWarnings) {
+ for (const auto &Chain : UninitFields) {
+ SmallString<100> WarningBuf;
+ llvm::raw_svector_ostream WarningOS(WarningBuf);
+
+ printNoteMessage(WarningOS, Chain);
+
+ auto Report = llvm::make_unique<BugReport>(
+ *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
+ Node->getLocationContext()->getDecl());
+ Context.emitReport(std::move(Report));
+ }
+ return;
+ }
+
+ 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());
+
+ for (const auto &Chain : UninitFields) {
+ SmallString<200> NoteBuf;
+ llvm::raw_svector_ostream NoteOS(NoteBuf);
+
+ printNoteMessage(NoteOS, Chain);
+
+ Report->addNote(NoteOS.str(),
+ PathDiagnosticLocation::create(Chain.getEndOfChain(),
+ Context.getSourceManager()));
+ }
+ Context.emitReport(std::move(Report));
+}
+
+//===----------------------------------------------------------------------===//
+// Methods for FindUninitializedFields.
+//===----------------------------------------------------------------------===//
+
+FindUninitializedFields::FindUninitializedFields(
+ ProgramStateRef State, const TypedValueRegion *const R, bool IsPedantic,
+ bool CheckPointeeInitialization)
+ : State(State), ObjectR(R), IsPedantic(IsPedantic),
+ CheckPointeeInitialization(CheckPointeeInitialization) {}
+
+const UninitFieldSet &FindUninitializedFields::getUninitFields() {
+ isNonUnionUninit(ObjectR, FieldChainInfo(Factory));
+
+ 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->isPointerType() || T->isReferenceType() || T->isBlockPointerType()) {
+ if (isPointerOrReferenceUninit(FR, LocalChain))
+ ContainsUninitField = true;
+ continue;
+ }
+
+ if (isPrimitiveType(T)) {
+ SVal V = State->getSVal(FieldVal);
+
+ if (isPrimitiveUninit(V)) {
+ if (addFieldToUninits({LocalChain, FR}))
+ ContainsUninitField = true;
+ }
+ continue;
+ }
+
+ llvm_unreachable("All cases are handled!");
+ }
+
+ // Checking bases.
+ // FIXME: As of now, because of `willObjectBeAnalyzedLater`, 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;
+}
+
+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 string if a void pointer is a
+// part of the chain:
+//
+// struct B { int x; }
+//
+// struct A {
+// void *vptr;
+// A(void* vptr) : vptr(vptr) {}
+// };
+//
+// void f() {
+// B b;
+// A a(&b);
+// }
+//
+// The note message will be "uninitialized field 'this->vptr->x'", even though
+// void pointers can't be dereferenced. This should be changed to "uninitialized
+// field 'static_cast<B*>(this->vptr)->x'".
+//
+// 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'.
+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 << getVariableName(L->getHead()->getDecl());
+}
+
+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 << getVariableName(Field);
+ Out << (Field->getType()->isPointerType() ? "->" : ".");
+}
+
+//===----------------------------------------------------------------------===//
+// Utility functions.
+//===----------------------------------------------------------------------===//
+
+static 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>();
+}
+
+static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
+ CheckerContext &Context) {
+
+ Optional<nonloc::LazyCompoundVal> CurrentObject = getObjectVal(Ctor, Context);
+ if (!CurrentObject)
+ return false;
+
+ const LocationContext *LC = Context.getLocationContext();
+ while ((LC = LC->getParent())) {
+
+ // If \p Ctor was called by another constructor.
+ const auto *OtherCtor = dyn_cast<CXXConstructorDecl>(LC->getDecl());
+ if (!OtherCtor)
+ continue;
+
+ Optional<nonloc::LazyCompoundVal> OtherObject =
+ getObjectVal(OtherCtor, Context);
+ if (!OtherObject)
+ continue;
+
+ // If the CurrentObject is a subregion of OtherObject, it will be analyzed
+ // during the analysis of OtherObject.
+ if (CurrentObject->getRegion()->isSubRegionOf(OtherObject->getRegion()))
+ return true;
+ }
+
+ return false;
+}
+
+static void printNoteMessage(llvm::raw_ostream &Out,
+ const FieldChainInfo &Chain) {
+ if (Chain.isPointer()) {
+ if (Chain.isDereferenced())
+ Out << "uninitialized pointee 'this->";
+ else
+ Out << "uninitialized pointer 'this->";
+ } else
+ Out << "uninitialized field 'this->";
+ Chain.print(Out);
+ Out << "'";
+}
+
+static StringRef getVariableName(const FieldDecl *Field) {
+ // If Field is a captured lambda variable, Field->getName() will return with
+ // an empty string. We can however acquire it's name from the lambda's
+ // captures.
+ const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent());
+
+ if (CXXParent && CXXParent->isLambda()) {
+ assert(CXXParent->captures_begin());
+ auto It = CXXParent->captures_begin() + Field->getFieldIndex();
+ return It->getCapturedVar()->getName();
+ }
+
+ return Field->getName();
+}
+
+void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
+ auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
+ Chk->IsPedantic = Mgr.getAnalyzerOptions().getBooleanOption(
+ "Pedantic", /*DefaultVal*/ false, Chk);
+ Chk->ShouldConvertNotesToWarnings = Mgr.getAnalyzerOptions().getBooleanOption(
+ "NotesAsWarnings", /*DefaultVal*/ false, Chk);
+ Chk->CheckPointeeInitialization = Mgr.getAnalyzerOptions().getBooleanOption(
+ "CheckPointeeInitialization", /*DefaultVal*/ false, Chk);
+}
Added: cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp?rev=339595&view=auto
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp (added)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp Mon Aug 13 11:17:05 2018
@@ -0,0 +1,171 @@
+//===----- UninitializedPointer.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 functions and methods for handling pointers and references
+// to reduce the size and complexity of UninitializedObjectChecker.cpp.
+//
+// To read about command line options and a description what this checker does,
+// refer to UninitializedObjectChecker.cpp.
+//
+// To read about how the checker works, refer to the comments in
+// UninitializedObject.h.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UninitializedObject.h"
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+
+using namespace clang;
+using namespace clang::ento;
+
+// Utility function declarations.
+
+/// Returns whether T 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.
+static bool isVoidPointer(QualType T);
+
+//===----------------------------------------------------------------------===//
+// Methods for FindUninitializedFields.
+//===----------------------------------------------------------------------===//
+
+// 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() ||
+ FR->getDecl()->getType()->isBlockPointerType()) &&
+ "This method only checks pointer/reference objects!");
+
+ SVal V = State->getSVal(FR);
+
+ if (V.isUnknown() || V.getAs<loc::ConcreteInt>()) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ if (V.isUndef()) {
+ return addFieldToUninits({LocalChain, FR});
+ }
+
+ if (!CheckPointeeInitialization) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ assert(V.getAs<loc::MemRegionVal>() &&
+ "At this point V must be loc::MemRegionVal!");
+ auto L = V.castAs<loc::MemRegionVal>();
+
+ // 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 (L.getRegion()->getSymbolicBase()) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ DynamicTypeInfo DynTInfo = getDynamicTypeInfo(State, L.getRegion());
+ if (!DynTInfo.isValid()) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ QualType DynT = DynTInfo.getType();
+
+ if (isVoidPointer(DynT)) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ // 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>(), DynT);
+
+ // If DerefdV is still a pointer value, we'll dereference it again (e.g.:
+ // int** -> int*).
+ while (auto Tmp = DerefdV.getAs<loc::MemRegionVal>()) {
+ if (Tmp->getRegion()->getSymbolicBase()) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ DynTInfo = getDynamicTypeInfo(State, Tmp->getRegion());
+ if (!DynTInfo.isValid()) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ DynT = DynTInfo.getType();
+ if (isVoidPointer(DynT)) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ DerefdV = State->getSVal(*Tmp, DynT);
+ }
+
+ // If FR is a pointer pointing to a non-primitive type.
+ if (Optional<nonloc::LazyCompoundVal> RecordV =
+ DerefdV.getAs<nonloc::LazyCompoundVal>()) {
+
+ const TypedValueRegion *R = RecordV->getRegion();
+
+ if (DynT->getPointeeType()->isStructureOrClassType())
+ return isNonUnionUninit(R, {LocalChain, FR});
+
+ if (DynT->getPointeeType()->isUnionType()) {
+ if (isUnionUninit(R)) {
+ return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true});
+ } else {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+ }
+
+ if (DynT->getPointeeType()->isArrayType()) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ llvm_unreachable("All cases are handled!");
+ }
+
+ assert((isPrimitiveType(DynT->getPointeeType()) || DynT->isPointerType() ||
+ DynT->isReferenceType()) &&
+ "At this point FR must either have a primitive dynamic type, or it "
+ "must be a null, undefined, unknown or concrete pointer!");
+
+ if (isPrimitiveUninit(DerefdV))
+ return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true});
+
+ IsAnyFieldInitialized = true;
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// Utility functions.
+//===----------------------------------------------------------------------===//
+
+static bool isVoidPointer(QualType T) {
+ while (!T.isNull()) {
+ if (T->isVoidPointerType())
+ return true;
+ T = T->getPointeeType();
+ }
+ return false;
+}
Removed: cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp?rev=339594&view=auto
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/UninitializedObjectChecker.cpp (removed)
@@ -1,776 +0,0 @@
-//===----- 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 several options:
-// - "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`.
-//
-// - "NotesAsWarnings" (boolean). If set to true, the checker will emit a
-// warning for each uninitalized field, as opposed to emitting one warning
-// per constructor call, and listing the uninitialized fields that belongs
-// to it in notes. Defaults to false.
-//
-// `-analyzer-config \
-// alpha.cplusplus.UninitializedObject:NotesAsWarnings=true`.
-//
-// - "CheckPointeeInitialization" (boolean). If set to false, the checker will
-// not analyze the pointee of pointer/reference fields, and will only check
-// whether the object itself is initialized. Defaults to false.
-//
-// `-analyzer-config \
-// alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=true`.
-//
-// TODO: With some clever heuristics, some pointers should be dereferenced
-// by default. For example, if the pointee is constructed within the
-// constructor call, it's reasonable to say that no external object
-// references it, and we wouldn't generate multiple report on the same
-// pointee.
-//
-//===----------------------------------------------------------------------===//
-
-#include "ClangSACheckers.h"
-#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
-#include "clang/StaticAnalyzer/Core/Checker.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
-
-using namespace clang;
-using namespace clang::ento;
-
-namespace {
-
-class UninitializedObjectChecker : public Checker<check::EndFunction> {
- std::unique_ptr<BuiltinBug> BT_uninitField;
-
-public:
- // These fields will be initialized when registering the checker.
- bool IsPedantic;
- bool ShouldConvertNotesToWarnings;
- bool CheckPointeeInitialization;
-
- UninitializedObjectChecker()
- : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {}
- void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
-};
-
-/// Represents a field chain. A field chain is a vector of fields where the
-/// first element of the chain is the object under checking (not stored), and
-/// every other element is a field, and the element that precedes it is the
-/// object that contains it.
-///
-/// Note that this class is immutable, and new fields may only be added through
-/// constructor calls.
-class FieldChainInfo {
-public:
- using FieldChain = llvm::ImmutableList<const FieldRegion *>;
-
-private:
- FieldChain::Factory &Factory;
- FieldChain Chain;
-
- const bool IsDereferenced = false;
-
-public:
- FieldChainInfo() = delete;
- FieldChainInfo(FieldChain::Factory &F) : Factory(F) {}
-
- FieldChainInfo(const FieldChainInfo &Other, const bool IsDereferenced)
- : Factory(Other.Factory), 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) const {
- 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;
- const bool CheckPointeeInitialization;
-
- bool IsAnyFieldInitialized = false;
-
- FieldChainInfo::FieldChain::Factory Factory;
- UninitFieldSet UninitFields;
-
-public:
- FindUninitializedFields(ProgramStateRef State,
- const TypedValueRegion *const R, bool IsPedantic,
- bool CheckPointeeInitialization);
- 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 primitive type, which we'll define later in a helper function.
- // * the parent of each node is the object that contains it
- // * every leaf is an array, a primitive object, a nullptr or an undefined
- // pointer.
- //
- // Example:
- //
- // struct A {
- // struct B {
- // int x, y = 0;
- // };
- // B b;
- // int *iptr = new int;
- // B* bptr;
- //
- // A() {}
- // };
- //
- // The directed tree:
- //
- // ->x
- // /
- // ->b--->y
- // /
- // A-->iptr->(int value)
- // \
- // ->bptr
- //
- // From this we'll construct a vector of fieldchains, where each fieldchain
- // represents an uninitialized field. An uninitialized field may be a
- // primitive object, 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 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.
-};
-
-} // end of anonymous namespace
-
-// Utility function declarations.
-
-/// Returns the object that was constructed by CtorDecl, or None if that isn't
-/// possible.
-// TODO: Refactor this function so that it returns the constructed object's
-// region.
-static Optional<nonloc::LazyCompoundVal>
-getObjectVal(const CXXConstructorDecl *CtorDecl, CheckerContext &Context);
-
-/// Checks whether the object constructed by \p Ctor will be analyzed later
-/// (e.g. if the object is a field of another object, in which case we'd check
-/// it multiple times).
-static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
- CheckerContext &Context);
-
-/// Returns whether T 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.
-static bool isVoidPointer(QualType T);
-
-/// Returns true if T is a primitive type. We defined this type so that for
-/// objects that we'd only like analyze as much as checking whether their
-/// value is undefined or not, such as ints and doubles, can be analyzed with
-/// ease. This also helps ensuring that every special field type is handled
-/// correctly.
-static bool isPrimitiveType(const QualType &T) {
- return T->isBuiltinType() || T->isEnumeralType() || T->isMemberPointerType();
-}
-
-/// Constructs a note message for a given FieldChainInfo object.
-static void printNoteMessage(llvm::raw_ostream &Out,
- const FieldChainInfo &Chain);
-
-/// Returns with Field's name. This is a helper function to get the correct name
-/// even if Field is a captured lambda variable.
-static StringRef getVariableName(const FieldDecl *Field);
-
-//===----------------------------------------------------------------------===//
-// Methods for UninitializedObjectChecker.
-//===----------------------------------------------------------------------===//
-
-void UninitializedObjectChecker::checkEndFunction(
- const ReturnStmt *RS, 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 (willObjectBeAnalyzedLater(CtorDecl, Context))
- return;
-
- Optional<nonloc::LazyCompoundVal> Object = getObjectVal(CtorDecl, Context);
- if (!Object)
- return;
-
- FindUninitializedFields F(Context.getState(), Object->getRegion(), IsPedantic,
- CheckPointeeInitialization);
-
- 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());
-
- // For Plist consumers that don't support notes just yet, we'll convert notes
- // to warnings.
- if (ShouldConvertNotesToWarnings) {
- for (const auto &Chain : UninitFields) {
- SmallString<100> WarningBuf;
- llvm::raw_svector_ostream WarningOS(WarningBuf);
-
- printNoteMessage(WarningOS, Chain);
-
- auto Report = llvm::make_unique<BugReport>(
- *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
- Node->getLocationContext()->getDecl());
- Context.emitReport(std::move(Report));
- }
- return;
- }
-
- 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());
-
- for (const auto &Chain : UninitFields) {
- SmallString<200> NoteBuf;
- llvm::raw_svector_ostream NoteOS(NoteBuf);
-
- printNoteMessage(NoteOS, Chain);
-
- Report->addNote(NoteOS.str(),
- PathDiagnosticLocation::create(Chain.getEndOfChain(),
- Context.getSourceManager()));
- }
- Context.emitReport(std::move(Report));
-}
-
-//===----------------------------------------------------------------------===//
-// Methods for FindUninitializedFields.
-//===----------------------------------------------------------------------===//
-
-FindUninitializedFields::FindUninitializedFields(
- ProgramStateRef State, const TypedValueRegion *const R, bool IsPedantic,
- bool CheckPointeeInitialization)
- : State(State), ObjectR(R), IsPedantic(IsPedantic),
- CheckPointeeInitialization(CheckPointeeInitialization) {}
-
-const UninitFieldSet &FindUninitializedFields::getUninitFields() {
- isNonUnionUninit(ObjectR, FieldChainInfo(Factory));
-
- 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->isPointerType() || T->isReferenceType() || T->isBlockPointerType()) {
- if (isPointerOrReferenceUninit(FR, LocalChain))
- ContainsUninitField = true;
- continue;
- }
-
- if (isPrimitiveType(T)) {
- SVal V = State->getSVal(FieldVal);
-
- if (isPrimitiveUninit(V)) {
- if (addFieldToUninits({LocalChain, FR}))
- ContainsUninitField = true;
- }
- continue;
- }
-
- llvm_unreachable("All cases are handled!");
- }
-
- // Checking bases.
- // FIXME: As of now, because of `willObjectBeAnalyzedLater`, 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() ||
- FR->getDecl()->getType()->isBlockPointerType()) &&
- "This method only checks pointer/reference objects!");
-
- SVal V = State->getSVal(FR);
-
- if (V.isUnknown() || V.getAs<loc::ConcreteInt>()) {
- IsAnyFieldInitialized = true;
- return false;
- }
-
- if (V.isUndef()) {
- return addFieldToUninits({LocalChain, FR});
- }
-
- if (!CheckPointeeInitialization) {
- IsAnyFieldInitialized = true;
- return false;
- }
-
- assert(V.getAs<loc::MemRegionVal>() &&
- "At this point V must be loc::MemRegionVal!");
- auto L = V.castAs<loc::MemRegionVal>();
-
- // 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 (L.getRegion()->getSymbolicBase()) {
- IsAnyFieldInitialized = true;
- return false;
- }
-
- DynamicTypeInfo DynTInfo = getDynamicTypeInfo(State, L.getRegion());
- if (!DynTInfo.isValid()) {
- IsAnyFieldInitialized = true;
- return false;
- }
-
- QualType DynT = DynTInfo.getType();
-
- if (isVoidPointer(DynT)) {
- IsAnyFieldInitialized = true;
- return false;
- }
-
- // 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>(), DynT);
-
- // If DerefdV is still a pointer value, we'll dereference it again (e.g.:
- // int** -> int*).
- while (auto Tmp = DerefdV.getAs<loc::MemRegionVal>()) {
- if (Tmp->getRegion()->getSymbolicBase()) {
- IsAnyFieldInitialized = true;
- return false;
- }
-
- DynTInfo = getDynamicTypeInfo(State, Tmp->getRegion());
- if (!DynTInfo.isValid()) {
- IsAnyFieldInitialized = true;
- return false;
- }
-
- DynT = DynTInfo.getType();
- if (isVoidPointer(DynT)) {
- IsAnyFieldInitialized = true;
- return false;
- }
-
- DerefdV = State->getSVal(*Tmp, DynT);
- }
-
- // If FR is a pointer pointing to a non-primitive type.
- if (Optional<nonloc::LazyCompoundVal> RecordV =
- DerefdV.getAs<nonloc::LazyCompoundVal>()) {
-
- const TypedValueRegion *R = RecordV->getRegion();
-
- if (DynT->getPointeeType()->isStructureOrClassType())
- return isNonUnionUninit(R, {LocalChain, FR});
-
- if (DynT->getPointeeType()->isUnionType()) {
- if (isUnionUninit(R)) {
- return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true});
- } else {
- IsAnyFieldInitialized = true;
- return false;
- }
- }
-
- if (DynT->getPointeeType()->isArrayType()) {
- IsAnyFieldInitialized = true;
- return false;
- }
-
- llvm_unreachable("All cases are handled!");
- }
-
- assert((isPrimitiveType(DynT->getPointeeType()) || DynT->isPointerType() ||
- DynT->isReferenceType()) &&
- "At this point FR must either have a primitive dynamic type, or it "
- "must be a null, undefined, unknown or concrete pointer!");
-
- if (isPrimitiveUninit(DerefdV))
- return addFieldToUninits({LocalChain, FR, /*IsDereferenced*/ true});
-
- IsAnyFieldInitialized = true;
- 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 string if a void pointer is a
-// part of the chain:
-//
-// struct B { int x; }
-//
-// struct A {
-// void *vptr;
-// A(void* vptr) : vptr(vptr) {}
-// };
-//
-// void f() {
-// B b;
-// A a(&b);
-// }
-//
-// The note message will be "uninitialized field 'this->vptr->x'", even though
-// void pointers can't be dereferenced. This should be changed to "uninitialized
-// field 'static_cast<B*>(this->vptr)->x'".
-//
-// 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'.
-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 << getVariableName(L->getHead()->getDecl());
-}
-
-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 << getVariableName(Field);
- Out << (Field->getType()->isPointerType() ? "->" : ".");
-}
-
-//===----------------------------------------------------------------------===//
-// Utility functions.
-//===----------------------------------------------------------------------===//
-
-static bool isVoidPointer(QualType T) {
- while (!T.isNull()) {
- if (T->isVoidPointerType())
- return true;
- T = T->getPointeeType();
- }
- return false;
-}
-
-static 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>();
-}
-
-static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
- CheckerContext &Context) {
-
- Optional<nonloc::LazyCompoundVal> CurrentObject = getObjectVal(Ctor, Context);
- if (!CurrentObject)
- return false;
-
- const LocationContext *LC = Context.getLocationContext();
- while ((LC = LC->getParent())) {
-
- // If \p Ctor was called by another constructor.
- const auto *OtherCtor = dyn_cast<CXXConstructorDecl>(LC->getDecl());
- if (!OtherCtor)
- continue;
-
- Optional<nonloc::LazyCompoundVal> OtherObject =
- getObjectVal(OtherCtor, Context);
- if (!OtherObject)
- continue;
-
- // If the CurrentObject is a subregion of OtherObject, it will be analyzed
- // during the analysis of OtherObject.
- if (CurrentObject->getRegion()->isSubRegionOf(OtherObject->getRegion()))
- return true;
- }
-
- return false;
-}
-
-static void printNoteMessage(llvm::raw_ostream &Out,
- const FieldChainInfo &Chain) {
- if (Chain.isPointer()) {
- if (Chain.isDereferenced())
- Out << "uninitialized pointee 'this->";
- else
- Out << "uninitialized pointer 'this->";
- } else
- Out << "uninitialized field 'this->";
- Chain.print(Out);
- Out << "'";
-}
-
-static StringRef getVariableName(const FieldDecl *Field) {
- // If Field is a captured lambda variable, Field->getName() will return with
- // an empty string. We can however acquire it's name from the lambda's
- // captures.
- const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent());
-
- if (CXXParent && CXXParent->isLambda()) {
- assert(CXXParent->captures_begin());
- auto It = CXXParent->captures_begin() + Field->getFieldIndex();
- return It->getCapturedVar()->getName();
- }
-
- return Field->getName();
-}
-
-void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
- auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
- Chk->IsPedantic = Mgr.getAnalyzerOptions().getBooleanOption(
- "Pedantic", /*DefaultVal*/ false, Chk);
- Chk->ShouldConvertNotesToWarnings = Mgr.getAnalyzerOptions().getBooleanOption(
- "NotesAsWarnings", /*DefaultVal*/ false, Chk);
- Chk->CheckPointeeInitialization = Mgr.getAnalyzerOptions().getBooleanOption(
- "CheckPointeeInitialization", /*DefaultVal*/ false, Chk);
-}
More information about the cfe-commits
mailing list