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