[clang-tools-extra] r266191 - Complete support for C++ Core Guidelines Type.6: Always initialize a member variable.

Alexander Kornienko via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 13 04:35:51 PDT 2016


Author: alexfh
Date: Wed Apr 13 06:35:47 2016
New Revision: 266191

URL: http://llvm.org/viewvc/llvm-project?rev=266191&view=rev
Log:
Complete support for C++ Core Guidelines Type.6: Always initialize a member variable.

Summary: Added the remaining features needed to satisfy C++ Core Guideline Type.6: Always initialize a member variable to cppcoreguidelines-pro-type-member-init. The check now flags all default-constructed uses of record types without user-provided default constructors that would leave their memory in an undefined state. The check suggests value initializing them instead.

Reviewers: flx, alexfh, aaron.ballman

Subscribers: klimek, aaron.ballman, LegalizeAdulthood, cfe-commits

Patch by Michael Miller!

Differential Revision: http://reviews.llvm.org/D18584

Modified:
    clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp
    clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.h
    clang-tools-extra/trunk/clang-tidy/utils/Matchers.h
    clang-tools-extra/trunk/clang-tidy/utils/TypeTraits.cpp
    clang-tools-extra/trunk/clang-tidy/utils/TypeTraits.h
    clang-tools-extra/trunk/docs/ReleaseNotes.rst
    clang-tools-extra/trunk/docs/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.rst
    clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init-cxx98.cpp
    clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init-delayed.cpp
    clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init.cpp

Modified: clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp?rev=266191&r1=266190&r2=266191&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp Wed Apr 13 06:35:47 2016
@@ -9,12 +9,15 @@
 
 #include "ProTypeMemberInitCheck.h"
 #include "../utils/LexerUtils.h"
+#include "../utils/Matchers.h"
+#include "../utils/TypeTraits.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Lex/Lexer.h"
 #include "llvm/ADT/SmallPtrSet.h"
 
 using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
 using llvm::SmallPtrSet;
 using llvm::SmallPtrSetImpl;
 
@@ -24,16 +27,13 @@ namespace cppcoreguidelines {
 
 namespace {
 
-AST_MATCHER(CXXConstructorDecl, isUserProvided) {
-  return Node.isUserProvided();
-}
-
-static void
-fieldsRequiringInit(const RecordDecl::field_range &Fields,
-                    SmallPtrSetImpl<const FieldDecl *> &FieldsToInit) {
+void fieldsRequiringInit(const RecordDecl::field_range &Fields,
+                         ASTContext &Context,
+                         SmallPtrSetImpl<const FieldDecl *> &FieldsToInit) {
   for (const FieldDecl *F : Fields) {
     QualType Type = F->getType();
-    if (Type->isPointerType() || Type->isBuiltinType())
+    if (!F->hasInClassInitializer() &&
+        type_traits::isTriviallyDefaultConstructible(Type, Context))
       FieldsToInit.insert(F);
   }
 }
@@ -50,134 +50,259 @@ void removeFieldsInitializedInBody(
     FieldDecls.erase(Match.getNodeAs<FieldDecl>("fieldDecl"));
 }
 
-// Creates comma separated list of fields requiring initialization in order of
+StringRef getName(const FieldDecl *Field) { return Field->getName(); }
+
+StringRef getName(const RecordDecl *Record) {
+  // Get the typedef name if this is a C-style anonymous struct and typedef.
+  if (const TypedefNameDecl *Typedef = Record->getTypedefNameForAnonDecl())
+    return Typedef->getName();
+  return Record->getName();
+}
+
+// Creates comma separated list of decls requiring initialization in order of
 // declaration.
-std::string toCommaSeparatedString(
-    const RecordDecl::field_range &FieldRange,
-    const SmallPtrSetImpl<const FieldDecl *> &FieldsRequiringInit) {
-  std::string List;
-  llvm::raw_string_ostream Stream(List);
-  size_t AddedFields = 0;
-  for (const FieldDecl *Field : FieldRange) {
-    if (FieldsRequiringInit.count(Field) > 0) {
-      Stream << Field->getName();
-      if (++AddedFields < FieldsRequiringInit.size())
-        Stream << ", ";
-    }
-  }
-  return Stream.str();
-}
-
-// Contains all fields in correct order that need to be inserted at the same
-// location for pre C++11.
-// There are 3 kinds of insertions:
-// 1. The fields are inserted after an existing CXXCtorInitializer stored in
-// InitializerBefore. This will be the case whenever there is a written
-// initializer before the fields available.
-// 2. The fields are inserted before the first existing initializer stored in
-// InitializerAfter.
-// 3. There are no written initializers and the fields will be inserted before
-// the constructor's body creating a new initializer list including the ':'.
-struct FieldsInsertion {
-  const CXXCtorInitializer *InitializerBefore;
-  const CXXCtorInitializer *InitializerAfter;
-  SmallVector<const FieldDecl *, 4> Fields;
+template <typename R, typename T>
+std::string
+toCommaSeparatedString(const R &OrderedDecls,
+                       const SmallPtrSetImpl<const T *> &DeclsToInit) {
+  SmallVector<StringRef, 16> Names;
+  for (const T *Decl : OrderedDecls) {
+    if (DeclsToInit.count(Decl))
+      Names.emplace_back(getName(Decl));
+  }
+  return llvm::join(Names.begin(), Names.end(), ", ");
+}
+
+SourceLocation getLocationForEndOfToken(const ASTContext &Context,
+                                        SourceLocation Location) {
+  return Lexer::getLocForEndOfToken(Location, 0, Context.getSourceManager(),
+                                    Context.getLangOpts());
+};
+
+// There are 3 kinds of insertion placements:
+enum class InitializerPlacement {
+  // 1. The fields are inserted after an existing CXXCtorInitializer stored in
+  // Where. This will be the case whenever there is a written initializer before
+  // the fields available.
+  After,
+
+  // 2. The fields are inserted before the first existing initializer stored in
+  // Where.
+  Before,
+
+  // 3. There are no written initializers and the fields will be inserted before
+  // the constructor's body creating a new initializer list including the ':'.
+  New
+};
+
+// An InitializerInsertion contains a list of fields and/or base classes to
+// insert into the initializer list of a constructor. We use this to ensure
+// proper absolute ordering according to the class declaration relative to the
+// (perhaps improper) ordering in the existing initializer list, if any.
+struct IntializerInsertion {
+  IntializerInsertion(InitializerPlacement Placement,
+                      const CXXCtorInitializer *Where)
+      : Placement(Placement), Where(Where) {}
 
   SourceLocation getLocation(const ASTContext &Context,
                              const CXXConstructorDecl &Constructor) const {
-    if (InitializerBefore != nullptr) {
-      return Lexer::getLocForEndOfToken(InitializerBefore->getRParenLoc(), 0,
-                                        Context.getSourceManager(),
-                                        Context.getLangOpts());
-    }
-    auto StartLocation = InitializerAfter != nullptr
-                             ? InitializerAfter->getSourceRange().getBegin()
-                             : Constructor.getBody()->getLocStart();
-    auto Token =
-        lexer_utils::getPreviousNonCommentToken(Context, StartLocation);
-    return Lexer::getLocForEndOfToken(Token.getLocation(), 0,
-                                      Context.getSourceManager(),
-                                      Context.getLangOpts());
+    assert((Where != nullptr || Placement == InitializerPlacement::New) &&
+           "Location should be relative to an existing initializer or this "
+           "insertion represents a new initializer list.");
+    SourceLocation Location;
+    switch (Placement) {
+    case InitializerPlacement::New:
+      Location = lexer_utils::getPreviousNonCommentToken(
+                     Context, Constructor.getBody()->getLocStart())
+                     .getLocation();
+      break;
+    case InitializerPlacement::Before:
+      Location = lexer_utils::getPreviousNonCommentToken(
+                     Context, Where->getSourceRange().getBegin())
+                     .getLocation();
+      break;
+    case InitializerPlacement::After:
+      Location = Where->getRParenLoc();
+      break;
+    }
+    return getLocationForEndOfToken(Context, Location);
   }
 
   std::string codeToInsert() const {
-    assert(!Fields.empty() && "No fields to insert");
+    assert(!Initializers.empty() && "No initializers to insert");
     std::string Code;
     llvm::raw_string_ostream Stream(Code);
-    // Code will be inserted before the first written initializer after ':',
-    // append commas.
-    if (InitializerAfter != nullptr) {
-      for (const auto *Field : Fields)
-        Stream << " " << Field->getName() << "(),";
-    } else {
-      // The full initializer list is created, add extra space after
-      // constructor's rparens.
-      if (InitializerBefore == nullptr)
-        Stream << " ";
-      for (const auto *Field : Fields)
-        Stream << ", " << Field->getName() << "()";
-    }
-    Stream.flush();
-    // The initializer list is created, replace leading comma with colon.
-    if (InitializerBefore == nullptr && InitializerAfter == nullptr)
-      Code[1] = ':';
+    std::string joined =
+        llvm::join(Initializers.begin(), Initializers.end(), "(), ");
+    switch (Placement) {
+    case InitializerPlacement::New:
+      Stream << " : " << joined << "()";
+      break;
+    case InitializerPlacement::Before:
+      Stream << " " << joined << "(),";
+      break;
+    case InitializerPlacement::After:
+      Stream << ", " << joined << "()";
+      break;
+    }
     return Code;
   }
+
+  InitializerPlacement Placement;
+  const CXXCtorInitializer *Where;
+  SmallVector<std::string, 4> Initializers;
 };
 
-SmallVector<FieldsInsertion, 16> computeInsertions(
-    const CXXConstructorDecl::init_const_range &Inits,
-    const RecordDecl::field_range &Fields,
-    const SmallPtrSetImpl<const FieldDecl *> &FieldsRequiringInit) {
-  // Find last written non-member initializer or null.
-  const CXXCtorInitializer *LastWrittenNonMemberInit = nullptr;
-  for (const CXXCtorInitializer *Init : Inits) {
-    if (Init->isWritten() && !Init->isMemberInitializer())
-      LastWrittenNonMemberInit = Init;
-  }
-  SmallVector<FieldsInsertion, 16> OrderedFields;
-  OrderedFields.push_back({LastWrittenNonMemberInit, nullptr, {}});
+// Convenience utility to get a RecordDecl from a QualType.
+const RecordDecl *getCanonicalRecordDecl(const QualType &Type) {
+  if (const auto *RT = Type.getCanonicalType()->getAs<RecordType>())
+    return RT->getDecl();
+  return nullptr;
+}
 
-  auto CurrentField = Fields.begin();
+template <typename R, typename T>
+SmallVector<IntializerInsertion, 16>
+computeInsertions(const CXXConstructorDecl::init_const_range &Inits,
+                  const R &OrderedDecls,
+                  const SmallPtrSetImpl<const T *> &DeclsToInit) {
+  SmallVector<IntializerInsertion, 16> Insertions;
+  Insertions.emplace_back(InitializerPlacement::New, nullptr);
+
+  typename R::const_iterator Decl = std::begin(OrderedDecls);
   for (const CXXCtorInitializer *Init : Inits) {
-    if (Init->isWritten() && Init->isMemberInitializer()) {
-      const FieldDecl *MemberField = Init->getMember();
-      // Add all fields between current field and this member field the previous
-      // FieldsInsertion if the field requires initialization.
-      for (; CurrentField != Fields.end() && *CurrentField != MemberField;
-           ++CurrentField) {
-        if (FieldsRequiringInit.count(*CurrentField) > 0)
-          OrderedFields.back().Fields.push_back(*CurrentField);
+    if (Init->isWritten()) {
+      if (Insertions.size() == 1)
+        Insertions.emplace_back(InitializerPlacement::Before, Init);
+
+      // Gets either the field or base class being initialized by the provided
+      // initializer.
+      const auto *InitDecl =
+          Init->isMemberInitializer()
+              ? static_cast<const NamedDecl *>(Init->getMember())
+              : Init->getBaseClass()->getAs<RecordType>()->getDecl();
+
+      // Add all fields between current field up until the next intializer.
+      for (; Decl != std::end(OrderedDecls) && *Decl != InitDecl; ++Decl) {
+        if (const T *D = dyn_cast<T>(*Decl)) {
+          if (DeclsToInit.count(D) > 0)
+            Insertions.back().Initializers.emplace_back(getName(D));
+        }
       }
-      // If this is the first written member initializer and there was no
-      // written non-member initializer set this initializer as
-      // InitializerAfter.
-      if (OrderedFields.size() == 1 &&
-          OrderedFields.back().InitializerBefore == nullptr)
-        OrderedFields.back().InitializerAfter = Init;
-      OrderedFields.push_back({Init, nullptr, {}});
+
+      Insertions.emplace_back(InitializerPlacement::After, Init);
     }
   }
-  // Add remaining fields that require initialization to last FieldsInsertion.
-  for (; CurrentField != Fields.end(); ++CurrentField) {
-    if (FieldsRequiringInit.count(*CurrentField) > 0)
-      OrderedFields.back().Fields.push_back(*CurrentField);
+
+  // Add remaining decls that require initialization.
+  for (; Decl != std::end(OrderedDecls); ++Decl) {
+    if (const T *D = dyn_cast<T>(*Decl)) {
+      if (DeclsToInit.count(D) > 0)
+        Insertions.back().Initializers.emplace_back(getName(D));
+    }
+  }
+  return Insertions;
+}
+
+// Gets the list of bases and members that could possibly be initialized, in
+// order as they appear in the class declaration.
+void getInitializationsInOrder(const CXXRecordDecl *ClassDecl,
+                               SmallVectorImpl<const NamedDecl *> &Decls) {
+  Decls.clear();
+  for (const auto &Base : ClassDecl->bases())
+    Decls.emplace_back(getCanonicalRecordDecl(Base.getType()));
+  Decls.append(ClassDecl->fields().begin(), ClassDecl->fields().end());
+}
+
+template <typename T>
+void fixInitializerList(const ASTContext &Context, DiagnosticBuilder &Diag,
+                        const CXXConstructorDecl *Ctor,
+                        const SmallPtrSetImpl<const T *> &DeclsToInit) {
+  // Do not propose fixes in macros since we cannot place them correctly.
+  if (Ctor->getLocStart().isMacroID())
+    return;
+
+  SmallVector<const NamedDecl *, 16> OrderedDecls;
+  getInitializationsInOrder(Ctor->getParent(), OrderedDecls);
+
+  for (const auto &Insertion :
+       computeInsertions(Ctor->inits(), OrderedDecls, DeclsToInit)) {
+    if (!Insertion.Initializers.empty())
+      Diag << FixItHint::CreateInsertion(Insertion.getLocation(Context, *Ctor),
+                                         Insertion.codeToInsert());
+  }
+}
+
+template <typename T, typename Func>
+void forEachField(const RecordDecl *Record, const T &Fields,
+                  bool OneFieldPerUnion, Func &&Fn) {
+  for (const FieldDecl *F : Fields) {
+    if (F->isAnonymousStructOrUnion()) {
+      if (const RecordDecl *R = getCanonicalRecordDecl(F->getType()))
+        forEachField(R, R->fields(), OneFieldPerUnion, Fn);
+    } else {
+      Fn(F);
+    }
+
+    if (OneFieldPerUnion && Record->isUnion())
+      break;
   }
-  return OrderedFields;
 }
 
 } // namespace
 
+ProTypeMemberInitCheck::ProTypeMemberInitCheck(StringRef Name,
+                                               ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      IgnoreArrays(Options.get("IgnoreArrays", false)) {}
+
 void ProTypeMemberInitCheck::registerMatchers(MatchFinder *Finder) {
-  Finder->addMatcher(cxxConstructorDecl(isDefinition(), isUserProvided(),
-                                        unless(isInstantiated()))
-                         .bind("ctor"),
-                     this);
+  if (!getLangOpts().CPlusPlus)
+    return;
+
+  auto IsUserProvidedNonDelegatingConstructor =
+      allOf(isUserProvided(),
+            unless(anyOf(isInstantiated(), isDelegatingConstructor())));
+  auto IsNonTrivialDefaultConstructor = allOf(
+      isDefaultConstructor(), unless(isUserProvided()),
+      hasParent(cxxRecordDecl(unless(isTriviallyDefaultConstructible()))));
+  Finder->addMatcher(
+      cxxConstructorDecl(isDefinition(),
+                         anyOf(IsUserProvidedNonDelegatingConstructor,
+                               IsNonTrivialDefaultConstructor))
+          .bind("ctor"),
+      this);
+  auto HasDefaultConstructor = hasInitializer(
+      cxxConstructExpr(unless(requiresZeroInitialization()),
+                       hasDeclaration(cxxConstructorDecl(
+                           isDefaultConstructor(), unless(isUserProvided())))));
+  Finder->addMatcher(
+      varDecl(isDefinition(), HasDefaultConstructor,
+              hasType(recordDecl(has(fieldDecl()),
+                                 isTriviallyDefaultConstructible())))
+          .bind("var"),
+      this);
 }
 
 void ProTypeMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
-  const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor");
-  const auto &MemberFields = Ctor->getParent()->fields();
+  if (const auto *Ctor = Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor")) {
+    checkMissingMemberInitializer(*Result.Context, Ctor);
+    checkMissingBaseClassInitializer(*Result.Context, Ctor);
+  } else if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("var")) {
+    checkUninitializedTrivialType(*Result.Context, Var);
+  }
+}
+
+void ProTypeMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "IgnoreArrays", IgnoreArrays);
+}
+
+void ProTypeMemberInitCheck::checkMissingMemberInitializer(
+    ASTContext &Context, const CXXConstructorDecl *Ctor) {
+  const CXXRecordDecl *ClassDecl = Ctor->getParent();
+  bool IsUnion = ClassDecl->isUnion();
+
+  if (IsUnion && ClassDecl->hasInClassInitializer())
+    return;
 
   // Skip declarations delayed by late template parsing without a body.
   const Stmt *Body = Ctor->getBody();
@@ -185,50 +310,113 @@ void ProTypeMemberInitCheck::check(const
     return;
 
   SmallPtrSet<const FieldDecl *, 16> FieldsToInit;
-  fieldsRequiringInit(MemberFields, FieldsToInit);
+  fieldsRequiringInit(ClassDecl->fields(), Context, FieldsToInit);
   if (FieldsToInit.empty())
     return;
 
-  for (CXXCtorInitializer *Init : Ctor->inits()) {
-    // Return early if this constructor simply delegates to another constructor
-    // in the same class.
-    if (Init->isDelegatingInitializer())
-      return;
-    if (!Init->isMemberInitializer())
-      continue;
-    FieldsToInit.erase(Init->getMember());
+  for (const CXXCtorInitializer *Init : Ctor->inits()) {
+    // Remove any fields that were explicitly written in the initializer list
+    // or in-class.
+    if (Init->isAnyMemberInitializer() && Init->isWritten()) {
+      if (IsUnion)
+        return; // We can only initialize one member of a union.
+      FieldsToInit.erase(Init->getMember());
+    }
   }
-  removeFieldsInitializedInBody(*Body, *Result.Context, FieldsToInit);
+  removeFieldsInitializedInBody(*Body, Context, FieldsToInit);
 
-  if (FieldsToInit.empty())
+  // Collect all fields in order, both direct fields and indirect fields from
+  // anonmyous record types.
+  SmallVector<const FieldDecl *, 16> OrderedFields;
+  forEachField(ClassDecl, ClassDecl->fields(), false,
+               [&](const FieldDecl *F) { OrderedFields.push_back(F); });
+
+  // Collect all the fields we need to initialize, including indirect fields.
+  SmallPtrSet<const FieldDecl *, 16> AllFieldsToInit;
+  forEachField(ClassDecl, FieldsToInit, false,
+               [&](const FieldDecl *F) { AllFieldsToInit.insert(F); });
+  if (AllFieldsToInit.empty())
     return;
 
   DiagnosticBuilder Diag =
       diag(Ctor->getLocStart(),
-           "constructor does not initialize these built-in/pointer fields: %0")
-      << toCommaSeparatedString(MemberFields, FieldsToInit);
+           IsUnion
+               ? "union constructor should initialize one of these fields: %0"
+               : "constructor does not initialize these fields: %0")
+      << toCommaSeparatedString(OrderedFields, AllFieldsToInit);
+
   // Do not propose fixes in macros since we cannot place them correctly.
   if (Ctor->getLocStart().isMacroID())
     return;
-  // For C+11 use in-class initialization which covers all future constructors
-  // as well.
-  if (Result.Context->getLangOpts().CPlusPlus11) {
-    for (const auto *Field : FieldsToInit) {
+
+  // Collect all fields but only suggest a fix for the first member of unions,
+  // as initializing more than one union member is an error.
+  SmallPtrSet<const FieldDecl *, 16> FieldsToFix;
+  forEachField(ClassDecl, FieldsToInit, true, [&](const FieldDecl *F) {
+    // Don't suggest fixes for enums because we don't know a good default.
+    if (!F->getType()->isEnumeralType())
+      FieldsToFix.insert(F);
+  });
+  if (FieldsToFix.empty())
+    return;
+
+  // Use in-class initialization if possible.
+  if (Context.getLangOpts().CPlusPlus11) {
+    for (const FieldDecl *Field : FieldsToFix) {
       Diag << FixItHint::CreateInsertion(
-          Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
-                                     Result.Context->getSourceManager(),
-                                     Result.Context->getLangOpts()),
+          getLocationForEndOfToken(Context, Field->getSourceRange().getEnd()),
           "{}");
     }
-    return;
+  } else {
+    // Otherwise, rewrite the constructor's initializer list.
+    fixInitializerList(Context, Diag, Ctor, FieldsToFix);
   }
-  for (const auto &FieldsInsertion :
-       computeInsertions(Ctor->inits(), MemberFields, FieldsToInit)) {
-    if (!FieldsInsertion.Fields.empty())
-      Diag << FixItHint::CreateInsertion(
-          FieldsInsertion.getLocation(*Result.Context, *Ctor),
-          FieldsInsertion.codeToInsert());
+}
+
+void ProTypeMemberInitCheck::checkMissingBaseClassInitializer(
+    const ASTContext &Context, const CXXConstructorDecl *Ctor) {
+  const CXXRecordDecl *ClassDecl = Ctor->getParent();
+
+  // Gather any base classes that need to be initialized.
+  SmallVector<const RecordDecl *, 4> AllBases;
+  SmallPtrSet<const RecordDecl *, 4> BasesToInit;
+  for (const CXXBaseSpecifier &Base : ClassDecl->bases()) {
+    if (const auto *BaseClassDecl = getCanonicalRecordDecl(Base.getType())) {
+      AllBases.emplace_back(BaseClassDecl);
+      if (!BaseClassDecl->field_empty() &&
+          type_traits::isTriviallyDefaultConstructible(Base.getType(), Context))
+        BasesToInit.insert(BaseClassDecl);
+    }
   }
+
+  if (BasesToInit.empty())
+    return;
+
+  // Remove any bases that were explicitly written in the initializer list.
+  for (const CXXCtorInitializer *Init : Ctor->inits()) {
+    if (Init->isBaseInitializer() && Init->isWritten())
+      BasesToInit.erase(Init->getBaseClass()->getAs<RecordType>()->getDecl());
+  }
+
+  if (BasesToInit.empty())
+    return;
+
+  DiagnosticBuilder Diag =
+      diag(Ctor->getLocStart(),
+           "constructor does not initialize these bases: %0")
+      << toCommaSeparatedString(AllBases, BasesToInit);
+
+  fixInitializerList(Context, Diag, Ctor, BasesToInit);
+}
+
+void ProTypeMemberInitCheck::checkUninitializedTrivialType(
+    const ASTContext &Context, const VarDecl *Var) {
+  DiagnosticBuilder Diag =
+      diag(Var->getLocStart(), "uninitialized record type: %0") << Var;
+
+  Diag << FixItHint::CreateInsertion(
+      getLocationForEndOfToken(Context, Var->getSourceRange().getEnd()),
+      Context.getLangOpts().CPlusPlus11 ? "{}" : " = {}");
 }
 
 } // namespace cppcoreguidelines

Modified: clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.h?rev=266191&r1=266190&r2=266191&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.h (original)
+++ clang-tools-extra/trunk/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.h Wed Apr 13 06:35:47 2016
@@ -16,8 +16,12 @@ namespace clang {
 namespace tidy {
 namespace cppcoreguidelines {
 
-/// \brief Checks that builtin or pointer fields are initialized by
-/// user-defined constructors.
+/// \brief Implements C++ Core Guidelines Type.6.
+///
+/// Checks that every user-provided constructor value-initializes all class
+/// members and base classes that would have undefined behavior otherwise. Also
+/// check that any record types without user-provided default constructors are
+/// value-initialized where used.
 ///
 /// Members initialized through function calls in the body of the constructor
 /// will result in false positives.
@@ -25,15 +29,40 @@ namespace cppcoreguidelines {
 /// For the user-facing documentation see:
 /// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.html
 /// TODO: See if 'fixes' for false positives are optimized away by the compiler.
-/// TODO: "Issue a diagnostic when constructing an object of a trivially
-/// constructible type without () or {} to initialize its members. To fix: Add
-/// () or {}."
+/// TODO: For classes with multiple constructors, make sure that we don't offer
+///     multiple in-class initializer fixits for the same  member.
 class ProTypeMemberInitCheck : public ClangTidyCheck {
 public:
-  ProTypeMemberInitCheck(StringRef Name, ClangTidyContext *Context)
-      : ClangTidyCheck(Name, Context) {}
+  ProTypeMemberInitCheck(StringRef Name, ClangTidyContext *Context);
   void registerMatchers(ast_matchers::MatchFinder *Finder) override;
   void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+
+private:
+  // Checks Type.6 part 1:
+  // Issue a diagnostic for any constructor of a non-trivially-constructible
+  // type that does not initialize all member variables.
+  //
+  // To fix: Write a data member initializer, or mention it in the member
+  // initializer list.
+  void checkMissingMemberInitializer(ASTContext &Context,
+                                     const CXXConstructorDecl *Ctor);
+
+  // A subtle side effect of Type.6 part 2:
+  // Make sure to initialize trivially constructible base classes.
+  void checkMissingBaseClassInitializer(const ASTContext &Context,
+                                        const CXXConstructorDecl *Ctor);
+
+  // Checks Type.6 part 2:
+  // Issue a diagnostic when constructing an object of a trivially constructible
+  // type without () or {} to initialize its members.
+  //
+  // To fix: Add () or {}.
+  void checkUninitializedTrivialType(const ASTContext &Context,
+                                     const VarDecl *Var);
+
+  // Whether arrays need to be initialized or not. Default is false.
+  bool IgnoreArrays;
 };
 
 } // namespace cppcoreguidelines

Modified: clang-tools-extra/trunk/clang-tidy/utils/Matchers.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/utils/Matchers.h?rev=266191&r1=266190&r2=266191&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/utils/Matchers.h (original)
+++ clang-tools-extra/trunk/clang-tidy/utils/Matchers.h Wed Apr 13 06:35:47 2016
@@ -23,6 +23,11 @@ AST_MATCHER(QualType, isExpensiveToCopy)
   return IsExpensive && *IsExpensive;
 }
 
+AST_MATCHER(RecordDecl, isTriviallyDefaultConstructible) {
+  return type_traits::recordIsTriviallyDefaultConstructible(
+      Node, Finder->getASTContext());
+}
+
 } // namespace matchers
 } // namespace tidy
 } // namespace clang

Modified: clang-tools-extra/trunk/clang-tidy/utils/TypeTraits.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/utils/TypeTraits.cpp?rev=266191&r1=266190&r2=266191&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/utils/TypeTraits.cpp (original)
+++ clang-tools-extra/trunk/clang-tidy/utils/TypeTraits.cpp Wed Apr 13 06:35:47 2016
@@ -31,6 +31,81 @@ llvm::Optional<bool> isExpensiveToCopy(Q
          !classHasTrivialCopyAndDestroy(Type);
 }
 
+bool recordIsTriviallyDefaultConstructible(const RecordDecl &RecordDecl,
+                                           const ASTContext &Context) {
+  const auto *ClassDecl = dyn_cast<CXXRecordDecl>(&RecordDecl);
+  // Non-C++ records are always trivially constructible.
+  if (!ClassDecl)
+    return true;
+  // A class with a user-provided default constructor is not trivially
+  // constructible.
+  if (ClassDecl->hasUserProvidedDefaultConstructor())
+    return false;
+  // A class is trivially constructible if it has a trivial default constructor.
+  if (ClassDecl->hasTrivialDefaultConstructor())
+    return true;
+
+  // If all its fields are trivially constructible.
+  for (const FieldDecl *Field : ClassDecl->fields()) {
+    if (!isTriviallyDefaultConstructible(Field->getType(), Context))
+      return false;
+  }
+  // If all its direct bases are trivially constructible.
+  for (const CXXBaseSpecifier &Base : ClassDecl->bases()) {
+    if (!isTriviallyDefaultConstructible(Base.getType(), Context))
+      return false;
+  }
+
+  return true;
+}
+
+// Based on QualType::isTrivial.
+bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context) {
+  if (Type.isNull())
+    return false;
+
+  if (Type->isArrayType())
+    return isTriviallyDefaultConstructible(Context.getBaseElementType(Type),
+                                           Context);
+
+  // Return false for incomplete types after skipping any incomplete array
+  // types which are expressly allowed by the standard and thus our API.
+  if (Type->isIncompleteType())
+    return false;
+
+  if (Context.getLangOpts().ObjCAutoRefCount) {
+    switch (Type.getObjCLifetime()) {
+    case Qualifiers::OCL_ExplicitNone:
+      return true;
+
+    case Qualifiers::OCL_Strong:
+    case Qualifiers::OCL_Weak:
+    case Qualifiers::OCL_Autoreleasing:
+      return false;
+
+    case Qualifiers::OCL_None:
+      if (Type->isObjCLifetimeType())
+        return false;
+      break;
+    }
+  }
+
+  QualType CanonicalType = Type.getCanonicalType();
+  if (CanonicalType->isDependentType())
+    return false;
+
+  // As an extension, Clang treats vector types as Scalar types.
+  if (CanonicalType->isScalarType() || CanonicalType->isVectorType())
+    return true;
+
+  if (const auto *RT = CanonicalType->getAs<RecordType>()) {
+    return recordIsTriviallyDefaultConstructible(*RT->getDecl(), Context);
+  }
+
+  // No other types can match.
+  return false;
+}
+
 } // type_traits
 } // namespace tidy
 } // namespace clang

Modified: clang-tools-extra/trunk/clang-tidy/utils/TypeTraits.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clang-tidy/utils/TypeTraits.h?rev=266191&r1=266190&r2=266191&view=diff
==============================================================================
--- clang-tools-extra/trunk/clang-tidy/utils/TypeTraits.h (original)
+++ clang-tools-extra/trunk/clang-tidy/utils/TypeTraits.h Wed Apr 13 06:35:47 2016
@@ -20,6 +20,13 @@ namespace type_traits {
 // \brief Returns true If \c Type is expensive to copy.
 llvm::Optional<bool> isExpensiveToCopy(QualType Type, ASTContext &Context);
 
+// \brief Returns true If \c Type is trivially default constructible.
+bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context);
+
+// \brief Returns true If \c RecordDecl is trivially default constructible.
+bool recordIsTriviallyDefaultConstructible(const RecordDecl &RecordDecl,
+                                           const ASTContext &Context);
+
 } // type_traits
 } // namespace tidy
 } // namespace clang

Modified: clang-tools-extra/trunk/docs/ReleaseNotes.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/ReleaseNotes.rst?rev=266191&r1=266190&r2=266191&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/ReleaseNotes.rst (original)
+++ clang-tools-extra/trunk/docs/ReleaseNotes.rst Wed Apr 13 06:35:47 2016
@@ -276,6 +276,22 @@ The 3.8 release didn't include release n
   * `readability-uniqueptr-delete-release
     <http://llvm.org/releases/3.8.0/tools/clang/tools/extra/docs/clang-tidy/checks/readability-uniqueptr-delete-release.html>`_
 
+- Updated ``cppcoreguidelines-pro-member-type-member-init`` check
+
+  This check now conforms to C++ Core Guidelines rule Type.6: Always Initialize
+  a Member Variable. The check examines every record type where construction
+  might result in an undefined memory state. These record types needing
+  initialization have at least one default-initialized built-in, pointer,
+  array or record type matching these criteria or a default-initialized
+  direct base class of this kind.
+
+  The check has two complementary aspects:
+  1. Ensure every constructor for a record type needing initialization
+     value-initializes all members and direct bases via a combination of
+     in-class initializers and the member initializer list.
+  2. Value-initialize every non-member instance of a record type needing
+     initialization that lacks a user-provided default constructor, e.g.
+     a POD.
 
 Improvements to modularize
 --------------------------

Modified: clang-tools-extra/trunk/docs/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.rst
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/docs/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.rst?rev=266191&r1=266190&r2=266191&view=diff
==============================================================================
--- clang-tools-extra/trunk/docs/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.rst (original)
+++ clang-tools-extra/trunk/docs/clang-tidy/checks/cppcoreguidelines-pro-type-member-init.rst Wed Apr 13 06:35:47 2016
@@ -3,16 +3,35 @@
 cppcoreguidelines-pro-type-member-init
 ======================================
 
-The check flags user-defined constructor definitions that do not initialize all
-builtin and pointer fields which leaves their memory in an undefined state.
+The check flags user-defined constructor definitions that do not
+initialize all fields that would be left in an undefined state by
+default construction, e.g. builtins, pointers and record types without
+user-provided default constructors containing at least one such
+type. If these fields aren't initialized, the constructor will leave
+some of the memory in an undefined state.
 
-For C++11 it suggests fixes to add in-class field initializers. For older
-versions it inserts the field initializers into the constructor initializer
-list.
-
-The check takes assignment of fields in the constructor body into account but
-generates false positives for fields initialized in methods invoked in the
-constructor body.
+For C++11 it suggests fixes to add in-class field initializers. For
+older versions it inserts the field initializers into the constructor
+initializer list. It will also initialize any direct base classes that
+need to be zeroed in the constructor initializer list.
 
-This rule is part of the "Type safety" profile of the C++ Core Guidelines, see
+The check takes assignment of fields in the constructor body into
+account but generates false positives for fields initialized in
+methods invoked in the constructor body.
+
+The check also flags variables of record types without a user-provided
+constructor that are not initialized. The suggested fix is to zero
+initialize the variable via {} for C++11 and beyond or = {} for older
+versions.
+
+IgnoreArrays option
+-------------------
+
+For performance critical code, it may be important to not zero
+fixed-size array members. If on, IgnoreArrays will not warn about
+array members that are not zero-initialized during construction.
+IgnoreArrays is false by default.
+
+This rule is part of the "Type safety" profile of the C++ Core
+Guidelines, corresponding to rule Type.6. See
 https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Pro-type-memberinit.

Modified: clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init-cxx98.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init-cxx98.cpp?rev=266191&r1=266190&r2=266191&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init-cxx98.cpp (original)
+++ clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init-cxx98.cpp Wed Apr 13 06:35:47 2016
@@ -3,13 +3,13 @@
 struct PositiveFieldBeforeConstructor {
   int F;
   PositiveFieldBeforeConstructor() /* some comment */ {}
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: F
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
   // CHECK-FIXES: PositiveFieldBeforeConstructor() : F() /* some comment */ {}
 };
 
 struct PositiveFieldAfterConstructor {
   PositiveFieldAfterConstructor() {}
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: F, G, H
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G, H
   // CHECK-FIXES: PositiveFieldAfterConstructor() : F(), G(), H() {}
   int F;
   bool G /* with comment */;
@@ -23,12 +23,12 @@ struct PositiveSeparateDefinition {
 };
 
 PositiveSeparateDefinition::PositiveSeparateDefinition() {}
-// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these built-in/pointer fields: F
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
 // CHECK-FIXES: PositiveSeparateDefinition::PositiveSeparateDefinition() : F() {}
 
 struct PositiveMixedFieldOrder {
   PositiveMixedFieldOrder() : /* some comment */ J(0), L(0), M(0) {}
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: I, K, N
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: I, K, N
   // CHECK-FIXES: PositiveMixedFieldOrder() : I(), /* some comment */ J(0), K(), L(0), M(0), N() {}
   int I;
   int J;
@@ -40,7 +40,7 @@ struct PositiveMixedFieldOrder {
 
 struct PositiveAfterBaseInitializer : public PositiveMixedFieldOrder {
   PositiveAfterBaseInitializer() : PositiveMixedFieldOrder() {}
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: F
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
   // CHECK-FIXES: PositiveAfterBaseInitializer() : PositiveMixedFieldOrder(), F() {}
   int F;
 };
@@ -64,4 +64,32 @@ struct NegativeInitializedInBody {
   int I;
 };
 
+struct NegativeAggregateType {
+  int X;
+  int Y;
+  int Z;
+};
+
+struct NonTrivialType {
+  int X;
+  int Y;
+};
+
+struct PositiveUninitializedBaseOrdering : public NegativeAggregateType,
+                                           public NonTrivialType {
+  PositiveUninitializedBaseOrdering() : NegativeAggregateType(), NonTrivialType(), B() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering() : NegativeAggregateType(), NonTrivialType(), A(), B() {}
+
+  // This is somewhat pathological with the base class initializer at the end...
+  PositiveUninitializedBaseOrdering(int) : B(), NonTrivialType(), A() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering(int) : B(), NegativeAggregateType(), NonTrivialType(), A() {}
+
+  PositiveUninitializedBaseOrdering(float) : NegativeAggregateType(), A() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NonTrivialType
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: constructor does not initialize these fields: B
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering(float) : NegativeAggregateType(), NonTrivialType(), A(), B() {}
 
+  int A, B;
+};

Modified: clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init-delayed.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init-delayed.cpp?rev=266191&r1=266190&r2=266191&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init-delayed.cpp (original)
+++ clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init-delayed.cpp Wed Apr 13 06:35:47 2016
@@ -1,20 +1,20 @@
 // RUN: %check_clang_tidy %s cppcoreguidelines-pro-type-member-init %t -- -- -fdelayed-template-parsing
 
-template<class T>
+template <class T>
 struct PositiveFieldBeforeConstructor {
-  PositiveFieldBeforeConstructor() {}
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: F, G, H
   int F;
   bool G /* with comment */;
   int *H;
+  PositiveFieldBeforeConstructor() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G, H
 };
 // Explicit instantiation.
 template class PositiveFieldBeforeConstructor<int>;
 
-template<class T>
+template <class T>
 struct PositiveFieldAfterConstructor {
   PositiveFieldAfterConstructor() {}
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: F, G, H
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G, H
   int F;
   bool G /* with comment */;
   int *H;
@@ -25,7 +25,7 @@ template class PositiveFieldAfterConstru
 // This declaration isn't used and won't be parsed 'delayed-template-parsing'.
 // The body of the declaration is 'null' and may cause crash if not handled
 // properly by checkers.
-template<class T>
+template <class T>
 struct UnusedDelayedConstructor {
   UnusedDelayedConstructor() {}
   int F;

Modified: clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init.cpp?rev=266191&r1=266190&r2=266191&view=diff
==============================================================================
--- clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init.cpp (original)
+++ clang-tools-extra/trunk/test/clang-tidy/cppcoreguidelines-pro-type-member-init.cpp Wed Apr 13 06:35:47 2016
@@ -4,13 +4,13 @@ struct PositiveFieldBeforeConstructor {
   int F;
   // CHECK-FIXES: int F{};
   PositiveFieldBeforeConstructor() {}
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: F
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
   // CHECK-FIXES: PositiveFieldBeforeConstructor() {}
 };
 
 struct PositiveFieldAfterConstructor {
   PositiveFieldAfterConstructor() {}
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: F, G
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F, G
   // CHECK-FIXES: PositiveFieldAfterConstructor() {}
   int F;
   // CHECK-FIXES: int F{};
@@ -26,12 +26,12 @@ struct PositiveSeparateDefinition {
 };
 
 PositiveSeparateDefinition::PositiveSeparateDefinition() {}
-// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these built-in/pointer fields: F
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
 // CHECK-FIXES: PositiveSeparateDefinition::PositiveSeparateDefinition() {}
 
 struct PositiveMixedFieldOrder {
   PositiveMixedFieldOrder() : J(0) {}
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: I, K
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: I, K
   // CHECK-FIXES: PositiveMixedFieldOrder() : J(0) {}
   int I;
   // CHECK-FIXES: int I{};
@@ -43,7 +43,7 @@ struct PositiveMixedFieldOrder {
 template <typename T>
 struct Template {
   Template() {}
-  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these built-in/pointer fields: F
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
   int F;
   // CHECK-FIXES: int F{};
   T T1;
@@ -67,7 +67,6 @@ struct NegativeFieldInitializedInDefinit
 };
 NegativeFieldInitializedInDefinition::NegativeFieldInitializedInDefinition() : F() {}
 
-
 struct NegativeInClassInitialized {
   int F = 0;
 
@@ -87,25 +86,248 @@ struct NegativeInitializedInBody {
 };
 
 #define UNINITIALIZED_FIELD_IN_MACRO_BODY(FIELD) \
-  struct UninitializedField##FIELD {		 \
-    UninitializedField##FIELD() {}		 \
-    int FIELD;					 \
-  };						 \
+  struct UninitializedField##FIELD {             \
+    UninitializedField##FIELD() {}               \
+    int FIELD;                                   \
+  };                                             \
 // Ensure FIELD is not initialized since fixes inside of macros are disabled.
 // CHECK-FIXES: int FIELD;
 
 UNINITIALIZED_FIELD_IN_MACRO_BODY(F);
-// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these built-in/pointer fields: F
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: F
 UNINITIALIZED_FIELD_IN_MACRO_BODY(G);
-// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these built-in/pointer fields: G
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: constructor does not initialize these fields: G
 
 #define UNINITIALIZED_FIELD_IN_MACRO_ARGUMENT(ARGUMENT) \
-  ARGUMENT						\
+  ARGUMENT
 
 UNINITIALIZED_FIELD_IN_MACRO_ARGUMENT(struct UninitializedFieldInMacroArg {
   UninitializedFieldInMacroArg() {}
   int Field;
 });
-// CHECK-MESSAGES: :[[@LINE-3]]:3: warning: constructor does not initialize these built-in/pointer fields: Field
+// CHECK-MESSAGES: :[[@LINE-3]]:3: warning: constructor does not initialize these fields: Field
 // Ensure FIELD is not initialized since fixes inside of macros are disabled.
 // CHECK-FIXES: int Field;
+
+struct NegativeAggregateType {
+  int X;
+  int Y;
+  int Z;
+};
+
+struct PositiveTrivialType {
+  PositiveTrivialType() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: F
+
+  NegativeAggregateType F;
+  // CHECK-FIXES: NegativeAggregateType F{};
+};
+
+struct NegativeNonTrivialType {
+  PositiveTrivialType F;
+};
+
+static void PositiveUninitializedTrivialType() {
+  NegativeAggregateType X;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'X'
+  // CHECK-FIXES: NegativeAggregateType X{};
+
+  NegativeAggregateType A[10]; // Don't warn because this isn't an object type.
+}
+
+static void NegativeInitializedTrivialType() {
+  NegativeAggregateType X{};
+  NegativeAggregateType Y = {};
+  NegativeAggregateType Z = NegativeAggregateType();
+  NegativeAggregateType A[10]{};
+  NegativeAggregateType B[10] = {};
+  int C; // No need to initialize this because we don't have a constructor.
+  int D[8];
+  NegativeAggregateType E = {0, 1, 2};
+  NegativeAggregateType F({});
+}
+
+struct NonTrivialType {
+  NonTrivialType() = default;
+  NonTrivialType(const NonTrivialType &RHS) : X(RHS.X), Y(RHS.Y) {}
+
+  int X;
+  int Y;
+};
+
+static void PositiveNonTrivialTypeWithCopyConstructor() {
+  NonTrivialType T;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'T'
+  // CHECK-FIXES: NonTrivialType T{};
+
+  NonTrivialType A[8];
+  // Don't warn because this isn't an object type
+}
+
+struct ComplexNonTrivialType {
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: constructor does not initialize these fields: Y
+  NegativeFieldInitialized X;
+  int Y;
+  // CHECK-FIXES: int Y{};
+};
+
+static void PositiveComplexNonTrivialType() {
+  ComplexNonTrivialType T;
+}
+
+struct PositiveStaticMember {
+  static NonTrivialType X;
+  static NonTrivialType Y;
+  static constexpr NonTrivialType Z{};
+};
+
+NonTrivialType PositiveStaticMember::X;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: uninitialized record type: 'X'
+// CHECK-FIXES: NonTrivialType PositiveStaticMember::X{};
+
+NonTrivialType PositiveStaticMember::Y{};
+
+struct PositiveMultipleConstructors {
+  PositiveMultipleConstructors() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+  PositiveMultipleConstructors(int) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+  PositiveMultipleConstructors(const PositiveMultipleConstructors &) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B
+
+  // FIXME: The fix-its here collide providing an erroneous fix
+  int A, B;
+  // CHECK-FIXES: int A{}{}{}, B{}{}{};
+};
+
+typedef struct {
+  int Member;
+} CStyleStruct;
+
+struct PositiveUninitializedBase : public NegativeAggregateType, CStyleStruct {
+  PositiveUninitializedBase() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType, CStyleStruct
+  // CHECK-FIXES: PositiveUninitializedBase() : NegativeAggregateType(), CStyleStruct() {}
+};
+
+struct PositiveUninitializedBaseOrdering : public NegativeAggregateType,
+                                           public NonTrivialType {
+  PositiveUninitializedBaseOrdering() : B() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType, NonTrivialType
+  // CHECK-MESSAGES: :[[@LINE-2]]:3: warning: constructor does not initialize these fields: A
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering() : NegativeAggregateType(), NonTrivialType(), B() {}
+
+  // This is somewhat pathological with the base class initializer at the end...
+  PositiveUninitializedBaseOrdering(int) : B(), NonTrivialType(), A() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NegativeAggregateType
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering(int) : B(), NegativeAggregateType(), NonTrivialType(), A() {}
+
+  PositiveUninitializedBaseOrdering(float) : B(), NegativeAggregateType(), A() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these bases: NonTrivialType
+  // CHECK-FIXES: PositiveUninitializedBaseOrdering(float) : B(), NegativeAggregateType(), NonTrivialType(), A() {}
+
+  int A, B;
+  // CHECK-FIXES: int A{}, B;
+};
+
+// We shouldn't need to initialize anything because PositiveUninitializedBase
+// has a user-defined constructor.
+struct NegativeUninitializedBase : public PositiveUninitializedBase {
+  NegativeUninitializedBase() {}
+};
+
+struct InheritedAggregate : public NegativeAggregateType {
+  int F;
+};
+
+static InheritedAggregate PositiveGlobal;
+// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: uninitialized record type: 'PositiveGlobal'
+// CHECK-FIXES: InheritedAggregate PositiveGlobal{};
+
+enum TestEnum {
+  A,
+  B,
+  C
+};
+
+enum class TestScopedEnum {
+  A,
+  B,
+  C
+};
+
+struct PositiveEnumType {
+  PositiveEnumType() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: X, Y
+  // No proposed fixes, as we don't know whether value initialization for these
+  // enums really makes sense.
+
+  TestEnum X;
+  TestScopedEnum Y;
+};
+
+extern "C" {
+struct NegativeCStruct {
+  int X, Y, Z;
+};
+
+static void PositiveCStructVariable() {
+  NegativeCStruct X;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: uninitialized record type: 'X'
+  // CHECK-FIXES: NegativeCStruct X{};
+}
+}
+
+union NegativeUnionInClass {
+  NegativeUnionInClass() {} // No message as a union can only initialize one member.
+  int X = 0;
+  float Y;
+};
+
+union PositiveUnion {
+  PositiveUnion() : X() {} // No message as a union can only initialize one member.
+  PositiveUnion(int) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: union constructor should initialize one of these fields: X, Y
+
+  int X;
+  // CHECK-FIXES: int X{};
+
+  // Make sure we don't give Y an initializer.
+  float Y;
+  // CHECK-FIXES-NOT: float Y{};
+};
+
+struct PositiveAnonymousUnionAndStruct {
+  PositiveAnonymousUnionAndStruct() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A, B, Y, Z, C, D, E, F, X
+
+  union {
+    int A;
+    // CHECK-FIXES: int A{};
+    short B;
+  };
+
+  struct {
+    int Y;
+    // CHECK-FIXES: int Y{};
+    char *Z;
+    // CHECK-FIXES: char *Z{};
+
+    struct {
+      short C;
+      // CHECK-FIXES: short C{};
+      double D;
+      // CHECK-FIXES: double D{};
+    };
+
+    union {
+      long E;
+      // CHECK-FIXES: long E{};
+      float F;
+    };
+  };
+  int X;
+  // CHECK-FIXES: int X{};
+};




More information about the cfe-commits mailing list