malcolm.parsons created this revision.
malcolm.parsons added reviewers: aaron.ballman, Eugene.Zelenko, alexfh.
malcolm.parsons added a subscriber: cfe-commits.
Herald added subscribers: modocache, mgorny.

Fixes PR18858


https://reviews.llvm.org/D26750

Files:
  clang-tidy/modernize/CMakeLists.txt
  clang-tidy/modernize/ModernizeTidyModule.cpp
  clang-tidy/modernize/UseDefaultMemberInitCheck.cpp
  clang-tidy/modernize/UseDefaultMemberInitCheck.h
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/modernize-use-default-member-init.rst
  test/clang-tidy/modernize-use-default-member-init-assignment.cpp
  test/clang-tidy/modernize-use-default-member-init.cpp

Index: test/clang-tidy/modernize-use-default-member-init.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/modernize-use-default-member-init.cpp
@@ -0,0 +1,202 @@
+// RUN: %check_clang_tidy %s modernize-use-default-member-init %t -- -- -std=c++11
+
+struct S {
+};
+
+struct PositiveValueInt {
+  PositiveValueInt() : i() {}
+  // CHECK-FIXES: PositiveValueInt()  {}
+  const int i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use default member initializer for 'i' [modernize-use-default-member-init]
+  // CHECK-FIXES: const int i{};
+};
+
+struct PositiveInt {
+  PositiveInt() : j(1) {}
+  // CHECK-FIXES: PositiveInt()  {}
+  int j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j' [modernize-use-default-member-init]
+  // CHECK-FIXES: int j{1};
+};
+
+struct PositiveValueDouble {
+  PositiveValueDouble() : d() {}
+  // CHECK-FIXES: PositiveValueDouble()  {}
+  double d;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'd' [modernize-use-default-member-init]
+  // CHECK-FIXES: double d{};
+};
+
+struct PositiveDouble {
+  PositiveDouble() : f(2.5463e43) {}
+  // CHECK-FIXES: PositiveDouble()  {}
+  double f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f' [modernize-use-default-member-init]
+  // CHECK-FIXES: double f{2.5463e43};
+};
+
+struct PositiveValueBool {
+  PositiveValueBool() : b() {}
+  // CHECK-FIXES: PositiveValueBool()  {}
+  bool b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'b' [modernize-use-default-member-init]
+  // CHECK-FIXES: bool b{};
+};
+
+struct PositiveBool {
+  PositiveBool() : a(true) {}
+  // CHECK-FIXES: PositiveBool()  {}
+  bool a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'a' [modernize-use-default-member-init]
+  // CHECK-FIXES: bool a{true};
+};
+
+struct PositiveValuePointer {
+  PositiveValuePointer() : p() {}
+  // CHECK-FIXES: PositiveValuePointer()  {}
+  int *p;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'p' [modernize-use-default-member-init]
+  // CHECK-FIXES: int *p{};
+};
+
+struct PositiveNullPointer {
+  PositiveNullPointer() : q(nullptr) {}
+  // CHECK-FIXES: PositiveNullPointer()  {}
+  int *q;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'q' [modernize-use-default-member-init]
+  // CHECK-FIXES: int *q{nullptr};
+};
+
+enum Enum { Foo, Bar };
+struct PositiveEnum {
+  PositiveEnum() : e(Foo) {}
+  // CHECK-FIXES: PositiveEnum()  {}
+  Enum e;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'e' [modernize-use-default-member-init]
+  // CHECK-FIXES: Enum e{Foo};
+};
+
+struct NegativeMemberInit {
+  NegativeMemberInit() {}
+  int i = 2;
+};
+
+struct NegativeClass : S {
+  NegativeClass() : s() {}
+  S s;
+};
+
+struct NegativeBase : S {
+  NegativeBase() : S() {}
+};
+
+struct NegativeDefaultOtherMember{
+  NegativeDefaultOtherMember() : i(3) {}
+  int i = 4;
+};
+
+struct NegativeUnion {
+  NegativeUnion() : d(5.0) {}
+  union {
+    int i;
+    double d;
+  };
+};
+
+struct NegativeBitField
+{
+  NegativeBitField() : i(6) {}
+  int i : 5;
+};
+
+struct NegativeNotDefaultInt
+{
+  NegativeNotDefaultInt(int) : i(7) {}
+  int i;
+};
+
+struct ExistingInt {
+  ExistingInt(short) : e1(), e2(), e3(), e4() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:24: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:30: warning: member initializer for 'e2' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingInt(short) :  e3(), e4() {}
+  ExistingInt(int) : e1(0), e2(0), e3(0), e4(0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:29: warning: member initializer for 'e2' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingInt(int) :  e3(0), e4(0) {}
+  ExistingInt(long) : e1(5), e2(5), e3(5), e4(5) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: member initializer for 'e3' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:44: warning: member initializer for 'e4' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingInt(long) : e1(5), e2(5) {}
+  int e1{};
+  int e2 = 0;
+  int e3 = {5};
+  int e4 = 5;
+};
+
+struct ExistingDouble {
+  ExistingDouble(short) : e1(), e2(), e3() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: member initializer for 'e2' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingDouble(short) :  e3() {}
+  ExistingDouble(int) : e1(0.0), e2(0.0), e3(0.0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingDouble(int) :  e3(0.0) {}
+  ExistingDouble(long) : e1(5.0), e2(5.0), e3(5.0) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: member initializer for 'e3' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingDouble(long) : e1(5.0), e2(5.0) {}
+  double e1{};
+  double e2 = 0.0;
+  double e3 = 5.0;
+};
+
+struct ExistingBool {
+  ExistingBool(short) : e1(), e2(), e3() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:31: warning: member initializer for 'e2' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingBool(short) :  e3() {}
+  ExistingBool(int) : e1(false), e2(false), e3(false) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:23: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingBool(int) :  e3(false) {}
+  ExistingBool(long) : e1(true), e2(true), e3(true) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: member initializer for 'e3' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingBool(long) : e1(true), e2(true) {}
+  bool e1{};
+  bool e2 = false;
+  bool e3 = true;
+};
+
+struct ExistingEnum {
+  ExistingEnum(short) : e1(Foo), e2(Foo) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingEnum(short) :  e2(Foo) {}
+  ExistingEnum(int) : e1(Bar), e2(Bar) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: member initializer for 'e2' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingEnum(int) : e1(Bar) {}
+  Enum e1 = Foo;
+  Enum e2{Bar};
+};
+
+struct ExistingPointer {
+  ExistingPointer(short) : e1(), e2(), e3(), e4() {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:34: warning: member initializer for 'e2' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-3]]:40: warning: member initializer for 'e3' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingPointer(short) :  e4() {}
+  ExistingPointer(int) : e1(0), e2(0), e3(0), e4(&e1) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:33: warning: member initializer for 'e2' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-3]]:40: warning: member initializer for 'e3' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingPointer(int) :  e4(&e1) {}
+  ExistingPointer(long) : e1(nullptr), e2(nullptr), e3(nullptr), e4(&e2) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: member initializer for 'e1' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-2]]:40: warning: member initializer for 'e2' is redundant [modernize-use-default-member-init]
+  // CHECK-MESSAGES: :[[@LINE-3]]:53: warning: member initializer for 'e3' is redundant [modernize-use-default-member-init]
+  // CHECK-FIXES: ExistingPointer(long) :  e4(&e2) {}
+  int *e1{};
+  int *e2 = 0;
+  int *e3 = nullptr;
+  int **e4 = &e1;
+};
Index: test/clang-tidy/modernize-use-default-member-init-assignment.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/modernize-use-default-member-init-assignment.cpp
@@ -0,0 +1,79 @@
+// RUN: %check_clang_tidy %s modernize-use-default-member-init %t -- \
+// RUN: -config="{CheckOptions: [{key: modernize-use-default-member-init.UseAssignment, value: 1}]}" -- -std=c++11
+
+struct S {
+};
+
+struct PositiveValueInt {
+  PositiveValueInt() : i() {}
+  // CHECK-FIXES: PositiveValueInt()  {}
+  const int i;
+  // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use default member initializer for 'i' [modernize-use-default-member-init]
+  // CHECK-FIXES: const int i = 0;
+};
+
+struct PositiveInt {
+  PositiveInt() : j(1) {}
+  // CHECK-FIXES: PositiveInt()  {}
+  int j;
+  // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use default member initializer for 'j' [modernize-use-default-member-init]
+  // CHECK-FIXES: int j = 1;
+};
+
+struct PositiveValueDouble {
+  PositiveValueDouble() : d() {}
+  // CHECK-FIXES: PositiveValueDouble()  {}
+  double d;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'd' [modernize-use-default-member-init]
+  // CHECK-FIXES: double d = 0.0;
+};
+
+struct PositiveDouble {
+  PositiveDouble() : f(2.5463e43) {}
+  // CHECK-FIXES: PositiveDouble()  {}
+  double f;
+  // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: use default member initializer for 'f' [modernize-use-default-member-init]
+  // CHECK-FIXES: double f = 2.5463e43;
+};
+
+struct PositiveValueBool {
+  PositiveValueBool() : b() {}
+  // CHECK-FIXES: PositiveValueBool()  {}
+  bool b;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'b' [modernize-use-default-member-init]
+  // CHECK-FIXES: bool b = false;
+};
+
+struct PositiveBool {
+  PositiveBool() : a(true) {}
+  // CHECK-FIXES: PositiveBool()  {}
+  bool a;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'a' [modernize-use-default-member-init]
+  // CHECK-FIXES: bool a = true;
+};
+
+struct PositiveValuePointer {
+  PositiveValuePointer() : p() {}
+  // CHECK-FIXES: PositiveValuePointer()  {}
+  int *p;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'p' [modernize-use-default-member-init]
+  // CHECK-FIXES: int *p = nullptr;
+};
+
+struct PositiveNullPointer {
+  PositiveNullPointer() : q(nullptr) {}
+  // CHECK-FIXES: PositiveNullPointer()  {}
+  int *q;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'q' [modernize-use-default-member-init]
+  // CHECK-FIXES: int *q = nullptr;
+};
+
+enum Enum { Foo };
+struct PositiveEnum {
+  PositiveEnum() : e(Foo) {}
+  // CHECK-FIXES: PositiveEnum()  {}
+  Enum e;
+  // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: use default member initializer for 'e' [modernize-use-default-member-init]
+  // CHECK-FIXES: Enum e = Foo;
+};
+
Index: docs/clang-tidy/checks/modernize-use-default-member-init.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/modernize-use-default-member-init.rst
@@ -0,0 +1,49 @@
+.. title:: clang-tidy - modernize-use-default-member-init
+
+modernize-use-default-member-init
+=================================
+
+This check converts a default constructor's member initializers into default
+member initializers.  Other member initializers that match the default
+member initializer are removed.  This can reduce repeated code or allow use of
+'= default'.
+
+.. code-block:: c++
+
+  struct A {
+    A() : i(5), j(10.0) {}
+    A(int i) : i(i), j(10.0) {}
+    int i;
+    double j;
+  };
+
+  // becomes
+
+  struct A {
+    A() {}
+    A(int i) : i(i) {}
+    int i{5};
+    double j{10.0};
+  };
+
+.. note::
+  Only converts member initializers for built-in types, enums and pointers.
+  The `readability-redundant-member-init` check will remove redundant member
+  initializers for classes.
+
+Options
+-------
+
+.. option:: UseAssignment
+
+   If this option is set to non-zero (default is `0`), the check will initialise
+   members with an assignment. For example:
+
+.. code-block:: c++
+
+  struct A {
+    A() {}
+    A(int i) : i(i) {}
+    int i = 5;
+    double j = 10.0;
+  };
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -110,6 +110,7 @@
    modernize-use-auto
    modernize-use-bool-literals
    modernize-use-default
+   modernize-use-default-member-init
    modernize-use-emplace
    modernize-use-equals-delete
    modernize-use-nullptr
Index: docs/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -91,6 +91,12 @@
   <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-auto.html>`_ check
   now warns about variable declarations that are initialized with a cast.
 
+- New `modernize-use-default-member-init
+  <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-default-member-init.html>`_ check
+
+  Converts a default constructor's member initializers into default member initializers.
+  Removes member initializers that are the same as a default member initializer.
+
 - New `modernize-use-equals-delete
   <http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-equals-delete.html>`_ check
 
Index: clang-tidy/modernize/UseDefaultMemberInitCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/modernize/UseDefaultMemberInitCheck.h
@@ -0,0 +1,45 @@
+//===--- UseDefaultMemberInitCheck.h - clang-tidy----------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+/// Convert a default constructor's member initializers into default member
+/// initializers.  Remove member initializers that are the same as a default
+/// member initializer.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-use-default-member-init.html
+class UseDefaultMemberInitCheck : public ClangTidyCheck {
+public:
+  UseDefaultMemberInitCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+  void checkDefaultInit(const ast_matchers::MatchFinder::MatchResult &Result,
+                        const CXXCtorInitializer *Init);
+  void checkExistingInit(const ast_matchers::MatchFinder::MatchResult &Result,
+                         const CXXCtorInitializer *Init);
+
+  const bool UseAssignment;
+};
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_DEFAULT_MEMBER_INIT_H
Index: clang-tidy/modernize/UseDefaultMemberInitCheck.cpp
===================================================================
--- /dev/null
+++ clang-tidy/modernize/UseDefaultMemberInitCheck.cpp
@@ -0,0 +1,189 @@
+//===--- UseDefaultMemberInitCheck.cpp - clang-tidy------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseDefaultMemberInitCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace modernize {
+
+static StringRef getValueInit(const CXXCtorInitializer *Init) {
+  switch (Init->getInit()->getType()->getScalarTypeKind()) {
+  case Type::STK_CPointer:
+  case Type::STK_BlockPointer:
+  case Type::STK_ObjCObjectPointer:
+  case Type::STK_MemberPointer:
+    return "nullptr";
+  case Type::STK_Bool:
+    return "false";
+  case Type::STK_Integral:
+    return "0";
+  case Type::STK_Floating:
+  case Type::STK_IntegralComplex:
+  case Type::STK_FloatingComplex:
+    return "0.0";
+  }
+  return "";
+}
+
+static bool isZero(const Expr *E) {
+  switch (E->getStmtClass()) {
+  case Stmt::CXXNullPtrLiteralExprClass:
+  case Stmt::ImplicitValueInitExprClass:
+    return true;
+  case Stmt::InitListExprClass:
+    return cast<InitListExpr>(E)->getNumInits() == 0;
+  case Stmt::CXXBoolLiteralExprClass:
+    return !cast<CXXBoolLiteralExpr>(E)->getValue();
+  case Stmt::IntegerLiteralClass:
+    return !cast<IntegerLiteral>(E)->getValue();
+  case Stmt::FloatingLiteralClass: {
+    llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue();
+    return Value.isZero() && !Value.isNegative();
+  }
+  default:
+    return false;
+  }
+}
+
+static const Expr *getInitializer(const Expr *E) {
+  auto *InitList = dyn_cast<InitListExpr>(E);
+  if (InitList && InitList->getNumInits() == 1) {
+    return InitList->getInit(0);
+  }
+  return E;
+}
+
+static bool sameValue(const Expr *E1, const Expr *E2) {
+  E1 = getInitializer(E1->IgnoreImpCasts());
+  E2 = getInitializer(E2->IgnoreImpCasts());
+
+  if (isZero(E1) && isZero(E2)) {
+    return true;
+  }
+
+  if (E1->getStmtClass() != E2->getStmtClass()) {
+    return false;
+  }
+
+  switch (E1->getStmtClass()) {
+  case Stmt::CXXBoolLiteralExprClass:
+    return cast<CXXBoolLiteralExpr>(E1)->getValue() ==
+           cast<CXXBoolLiteralExpr>(E2)->getValue();
+  case Stmt::IntegerLiteralClass:
+    return cast<IntegerLiteral>(E1)->getValue() ==
+           cast<IntegerLiteral>(E2)->getValue();
+  case Stmt::FloatingLiteralClass:
+    return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual(
+        cast<FloatingLiteral>(E2)->getValue());
+  case Stmt::DeclRefExprClass:
+    return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();
+  default:
+    break;
+  }
+  return false;
+}
+
+UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name,
+                                                     ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      UseAssignment(Options.get("UseAssignment", 0) != 0) {}
+
+void UseDefaultMemberInitCheck::storeOptions(
+    ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "UseAssignment", UseAssignment);
+}
+
+AST_MATCHER(FieldDecl, hasInClassInitializer) {
+  return Node.hasInClassInitializer();
+}
+
+void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      cxxConstructorDecl(
+          isDefaultConstructor(),
+          forEachConstructorInitializer(allOf(
+              forField(unless(anyOf(isBitField(), hasInClassInitializer()))),
+              cxxCtorInitializer(isWritten(),
+                                 withInitializer(ignoringImplicit(anyOf(
+                                     integerLiteral(), floatLiteral(),
+                                     cxxBoolLiteral(), cxxNullPtrLiteralExpr(),
+                                     implicitValueInitExpr(), declRefExpr()))))
+                  .bind("default")))),
+      this);
+
+  Finder->addMatcher(
+      cxxConstructorDecl(forEachConstructorInitializer(
+          allOf(forField(hasInClassInitializer()),
+                cxxCtorInitializer(
+                    isWritten(), withInitializer(ignoringImplicit(anyOf(
+                                     integerLiteral(), floatLiteral(),
+                                     cxxBoolLiteral(), cxxNullPtrLiteralExpr(),
+                                     implicitValueInitExpr(), declRefExpr()))))
+                    .bind("existing")))),
+      this);
+}
+
+void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Default = Result.Nodes.getNodeAs<CXXCtorInitializer>("default");
+  const auto *Existing = Result.Nodes.getNodeAs<CXXCtorInitializer>("existing");
+
+  if (Default)
+    checkDefaultInit(Result, Default);
+  else
+    checkExistingInit(Result, Existing);
+}
+
+void UseDefaultMemberInitCheck::checkDefaultInit(
+    const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
+  const FieldDecl *Field = Init->getMember();
+
+  SourceLocation FieldEnd =
+      Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
+                                 *Result.SourceManager, getLangOpts());
+  SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
+      Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
+  CharSourceRange InitRange =
+      CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
+
+  auto Diag =
+      diag(Field->getLocation(), "use default member initializer for %0")
+      << Field
+      << FixItHint::CreateInsertion(FieldEnd, UseAssignment ? " = " : "{")
+      << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
+
+  if (UseAssignment && isa<ImplicitValueInitExpr>(Init->getInit()))
+    Diag << FixItHint::CreateInsertion(FieldEnd, getValueInit(Init));
+
+  if (!UseAssignment)
+    Diag << FixItHint::CreateInsertion(FieldEnd, "}");
+
+  Diag << FixItHint::CreateRemoval(Init->getSourceRange());
+}
+
+void UseDefaultMemberInitCheck::checkExistingInit(
+    const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
+  const FieldDecl *Field = Init->getMember();
+
+  if (!sameValue(Field->getInClassInitializer(), Init->getInit()))
+    return;
+
+  diag(Init->getSourceLocation(), "member initializer for %0 is redundant")
+      << Field
+      << FixItHint::CreateRemoval(Init->getSourceRange());
+}
+
+} // namespace modernize
+} // namespace tidy
+} // namespace clang
Index: clang-tidy/modernize/ModernizeTidyModule.cpp
===================================================================
--- clang-tidy/modernize/ModernizeTidyModule.cpp
+++ clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -23,6 +23,7 @@
 #include "UseAutoCheck.h"
 #include "UseBoolLiteralsCheck.h"
 #include "UseDefaultCheck.h"
+#include "UseDefaultMemberInitCheck.h"
 #include "UseEmplaceCheck.h"
 #include "UseEqualsDeleteCheck.h"
 #include "UseNullptrCheck.h"
@@ -56,6 +57,8 @@
     CheckFactories.registerCheck<UseBoolLiteralsCheck>(
         "modernize-use-bool-literals");
     CheckFactories.registerCheck<UseDefaultCheck>("modernize-use-default");
+    CheckFactories.registerCheck<UseDefaultMemberInitCheck>(
+        "modernize-use-default-member-init");
     CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
     CheckFactories.registerCheck<UseEqualsDeleteCheck>(
         "modernize-use-equals-delete");
Index: clang-tidy/modernize/CMakeLists.txt
===================================================================
--- clang-tidy/modernize/CMakeLists.txt
+++ clang-tidy/modernize/CMakeLists.txt
@@ -17,6 +17,7 @@
   UseAutoCheck.cpp
   UseBoolLiteralsCheck.cpp
   UseDefaultCheck.cpp
+  UseDefaultMemberInitCheck.cpp
   UseEmplaceCheck.cpp
   UseEqualsDeleteCheck.cpp
   UseNullptrCheck.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to